參考鏈接: Java中的for-each循環
1. 引入?
?正如Java語法意義,變量的傳遞只有值傳遞,雖然變量分為引用變量和基本類型變量,前者更像C中的地址概念。 在學習Lambda表達式的時候,遇到了試圖在增強for循環中對原鏈表元素重新賦值失敗的問題,網絡上也沒有針對此的其他博文,故開此文。?
2. 數組的增強for循環?
public class Test1{
?
? ? public static void main(String[] args) {
? ??
? ? ? ? int[] arr = new int[10];
?
? ? ? ? for (int temp :arr){
? ? ? ? ? ? temp++;
? ? ? ? }
?
? ? ? ? for (int temp :arr){
? ? ? ? ? ? System.out.println(temp);
? ? ? ? }
?
?
? ? }
}
?
?控制臺會打出10個0,而不是1,這表明在forEach語句中temp++操作對arr數組本身沒有任何影響,所以間接證明了,增強for循環中只是值傳遞。這也可以從原理層面解釋:增強for循環作為一個語法糖,其執行順序是:對數組第一個元素復制給臨時變量temp,然后讓temp執行循環中的語句;接著對數組第二個元素再次賦值給臨時變量temp,再次讓其執行for循環中的語句…就這般執行至數組最后一個元素。所以說,temp接受了數組元素的值,在++,這對于數組中的數字沒有任何影響。所以說如果要進行原數組的更改,更好的方式是使用普通的for循環。?
3. ArrayList的增強for循環?
?代碼需求是將其list中的String類型對象從小寫轉換為大寫;?
public class LowercaseToUppercase{
?
? ? public static void main(String[] args) {
?
? ? ? ? List<String> list = Arrays.asList("hello", "world", "hello world");
?
? ? ? ? list.forEach(i -> {
? ? ? ? ? ? i = i.toUpperCase();
? ? ? ? });
?
? ? ? ? list.forEach(System.out::println);
? ? }
}
?
?
?控制還是輸出小寫的String類型對象,“hello”, “world”, “hello world”,倘若你查看forEach方法,你可以發現此原理和第一個例子的數組遍歷實現原理是一樣的,i作為一個中間變量,是臨時存放了String類型的引用變量,但是對原list沒有任何影響,如下面被調用的forEach方法的默認實現代碼(其中t就是被定義為泛型類型T的臨時變量)。 ?一個易錯點:很多人認為:因為String內部是final修飾的數組,不能被重新賦值,臨時變量i只能指向新的引用對象,所以上述代碼功能才不能被實現,這是不對的,其真正的原因是對臨時變量賦值是無法達到預期效果。正確的理解是:對臨時變量進行賦值,只能使臨時變量指向新的對象,而對原String對象沒有任何作用。即使將上述代碼中ArrayList的對象類型由String換成StringBuilder類,在這樣的情況下,雖然同一個```StringBuilder``對象的值是可以被修改的,但是使用對臨時變量賦值的操作還是不能對原數據結構元素值造成影響。?
?如果要實現,需要調用StringBuilder類對象的方法,一般是返回this對象,代碼如下所示:?
public class LowercaseToUppercase {
?
? ? public static void main(String[] args) {
? ?
? ? ? ? List<StringBuilder> list3= Arrays.asList(new StringBuilder("hello"),
? ? ? ? new StringBuilder("world"),new StringBuilder("hello world"));
?
? ? ? ? list3.forEach(i->
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? String str= i.toString().toUpperCase();
? ? ? ? ? ? ? ? ? ? i.replace(0,str.length(),str);
? ? ? ? ? ? ? ? }
?
? ? ? ? );
? ? ? ? list3.forEach(System.out::println);
?
? ? }
}
?
?
?控制臺輸出了大寫的字符串,說明我們成功將StringBuilder類型由小寫轉化為大寫,不過遍歷中的臨時變量i的賦值語句并不存在,而是調用其方法,返回this對象,才實現了轉換。?
?下面這個代碼塊是Java集合的forEach方法默認實現,一定要讀懂它:?
? ?default void forEach(Consumer<? super T> action) {
? ? ? ? Objects.requireNonNull(action);
? ? ? ? for (T t : this) {
? ? ? ? ? ? action.accept(t);
? ? ? ? }
? ? }
?
?可見Java在foreach語言的執行上保證了原數據結構的安全性,如果確定要更改原數據結構,請使用傳統的for循環。并且我們在foreach語句中可以采用復制給新數據結構的方法實現類似的作用:?
? ? ? ? List<String> list2 = new ArrayList<>();
?
? ? ? ? list.forEach(item->list2.add(item.toUpperCase()));
?
? ? ? ? list2.forEach(System.out::println);
?
?倘若返回list2,那么和傳統的for語句也是類似的效果。