最近在看《Thinking in Java》中關于容器的章節(第11章 持有對象),有一個例子發現subList中資料順序的改變會影響原list中資料的順序。下面總結如下。
結論
- 使用List.subList方法得到的子序列其實隻是将指針指向了原list,并設定了這個sub list的大小。
- 使用Arrays.asList(數組的引用)這種方式生成連結清單時,該連結清單仍然指向這個資料,是以,對這個連結清單中資料順序的改變會影響原數組中元素的順序。
測試代碼
package org.fan.learn.shuffle;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* Created by fan on 16/3/20.
*/
public class Main {
public static void main(String[] args) {
Integer[] ints = {, , , };
List<Integer> list = new ArrayList<Integer>();
Collections.addAll(list, ints); //list與ints是兩個不同的記憶體區域
System.out.println(list); //list: [1, 2, 3, 4]
System.out.println(Arrays.toString(ints)); //ints: [1, 2, 3, 4]
List<Integer> sub = list.subList(,);
System.out.println("sub before reverse : " + sub); //sub before reverse : [2, 3]
Collections.reverse(sub);
System.out.println("sub after reverse : " + sub); //sub after reverse : [3, 2]
System.out.println("list : " + list); //會影響list:[1, 3, 2, 4]
System.out.println(Arrays.toString(ints));//不會影響ints: [1, 2, 3, 4]
//此時的list中的值為:[, , , ]
//此時的ints中的值為:[, , , ]
List<Integer> sub2 = Arrays.asList(ints); //sub2 與 ints指向的是同一塊記憶體
System.out.println("sub2 before reverse : " + sub2); //sub2 before reverse : [1, 2, 3, 4]
Collections.reverse(sub2); //
System.out.println("sub2 after reverse : " + sub2); //sub2 after reverse : [4, 3, 2, 1]
System.out.println("list : " + list); //list : [1, 3, 2, 4]
System.out.println(Arrays.toString(ints)); //[4, 3, 2, 1]
//此時的list中的值為:[, , , ]
//此時的ints中的值為:[, , , ]
List<Integer> sub3 = Arrays.asList(list.get(), list.get()); //sub3不會影響list
System.out.println("sub3 before reverse : " + sub3); //sub3 before reverse : [3, 2]
Collections.reverse(sub3); //
System.out.println("sub3 after reverse : " + sub3); //sub3 after reverse : [2, 3]
System.out.println("list : " + list); //list : [1, 3, 2, 4]
System.out.println(Arrays.toString(ints)); //[4, 3, 2, 1]
//此時的list中的值為:[, , , ]
//此時的ints中的值為:[, , , ]
List<Integer> sub4 = new ArrayList<Integer>(list.subList(,)); //sub4不會影響list
System.out.println("sub4 before reverse : " + sub4); //sub4 before reverse : [3, 2]
Collections.reverse(sub4); //
System.out.println("sub4 after reverse : " + sub4); //sub4 after reverse : [2, 3]
System.out.println("list : " + list); //list : [1, 3, 2, 4]
System.out.println(Arrays.toString(ints)); //[4, 3, 2, 1]
}
}
分析
上面代碼的記憶體模型應該是下面這個樣子(下圖不夠準确):
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISO4QjM1YjM0ETMyMDM2EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
由此可見sub與list指向同一塊記憶體。是以,對sub内容的改變會影響list。
下面代碼:
import java.util.*;
public class TestList {
public static void main(String[] args) {
Integer[] ints = {, , , };
List<Integer> list = new ArrayList<Integer>();
Collections.addAll(list, ints);
}
}
其位元組碼如下所示:
public static void main(java.lang.String[]);
Code:
Stack=, Locals=, Args_size=
: iconst_4
: anewarray #2; //class java/lang/Integer
4: dup
: iconst_0
: iconst_1
: invokestatic #3; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
10: aastore
: dup
: iconst_1
: iconst_2
: invokestatic #3; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
17: aastore
: dup
: iconst_2
: iconst_3
: invokestatic #3; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
24: aastore
: dup
: iconst_3
: iconst_4
: invokestatic #3; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
31: aastore
: astore_1
: new #4; //class java/util/ArrayList
36: dup
: invokespecial #5; //Method java/util/ArrayList."<init>":()V
40: astore_2
: aload_2
: aload_1
: invokestatic #6; //Method java/util/Collections.addAll:(Ljava/util/Collection;[Ljava/lang/Object;)Z
46: pop
: return
1.由此可見,對于整型常亮1 2 3 4,在java位元組碼中直接使用iconst_X(X代表具體整型值)來表示。
2.而且在new Integer數組時,直接指定了數組的大小,如下所示:
0: iconst_4
1: anewarray #2; //class java/lang/Integer
是以,數組是不可以擴容的。
3.當1 2 3 4放入Integer數組時,有一個類型轉換的過程,如下所示:
6: iconst_1
7: invokestatic #3; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
這就是所謂的自動裝箱(Autoboxing)。
update-20160523
寫這邊博文到現在已經過去了一段時間,今日看到有博樂推薦我這篇博文,很是感激。于是又看了一遍,發現有些點又有些生疏了。下面記錄如下:
關于aastore
第一個a表示數組中存放的資料類型,a是reference的意思。
第二個a表示array,表示這是一個數組操作。
store就是将元素存入數組了。
aastore:
Description: store value in array[index]
解釋如下:
參考的資料:aastore
在使用這個aastore時,必須要先入棧數組變量,然後入棧下标index,然後入棧所需要存放的值。
如在這個例子中:
0: iconst_4
1: anewarray #2; //class java/lang/Integer
4: dup
5: iconst_0
6: iconst_1
7: invokestatic #3; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
10: aastore
首先new出數組變量ints,然後dup一下,複制這個數組變量并入棧,此時的棧中有兩個數組變量ints。然後入棧常量0,表示要操作的數組ints的下标。然後入棧常量1,表示要往數組中存放的資料。由于ints中存放的是封裝類型Integer,是以需要将int類型的1,自動轉型成Integer類型的。這時棧中的資料從棧底到棧頂依次是:ints,ints,0(int型),1(Integer型)。然後調用aastore,将Integer類型的1存入下标為0的ints中。這時棧中隻有一個ints。
雖然更新了一次,但是感覺本質問題(影響順序)沒有解釋的清清楚楚,是以又寫了一篇博文闡述:子list中的順序會影響list的順序問題(二)