子list中的顺序会影响list的顺序问题
最近在看《Thinking in Java》中关于容器的章节(第11章 持有对象),有一个例子发现subList中数据顺序的改变会影响原list中数据的顺序。下面总结如下。结论使用List.subList方法得到的子序列其实只是将指针指向了原list,并设置了这个sub list的大小。使用Arrays.asList(数组的引用)这种方式生成链表时,该链表仍然指向这个数据,因此,对这个链表中数据
最近在看《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 = {1, 2, 3, 4};
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(1,3);
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中的值为:[1, 3, 2, 4]
//此时的ints中的值为:[1, 2, 3, 4]
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中的值为:[1, 3, 2, 4]
//此时的ints中的值为:[4, 3, 2, 1]
List<Integer> sub3 = Arrays.asList(list.get(1), list.get(2)); //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中的值为:[1, 3, 2, 4]
//此时的ints中的值为:[4, 3, 2, 1]
List<Integer> sub4 = new ArrayList<Integer>(list.subList(1,3)); //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]
}
}
分析
List<Integer> sub = list.subList(1,3);
上面代码的内存模型应该是下面这个样子(下图不够准确):
由此可见sub与list指向同一块内存。所以,对sub内容的改变会影响list。
下面代码:
import java.util.*;
public class TestList {
public static void main(String[] args) {
Integer[] ints = {1, 2, 3, 4};
List<Integer> list = new ArrayList<Integer>();
Collections.addAll(list, ints);
}
}
其字节码如下所示:
public static void main(java.lang.String[]);
Code:
Stack=4, Locals=3, Args_size=1
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
11: dup
12: iconst_1
13: iconst_2
14: invokestatic #3; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
17: aastore
18: dup
19: iconst_2
20: iconst_3
21: invokestatic #3; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
24: aastore
25: dup
26: iconst_3
27: iconst_4
28: invokestatic #3; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
31: aastore
32: astore_1
33: new #4; //class java/util/ArrayList
36: dup
37: invokespecial #5; //Method java/util/ArrayList."<init>":()V
40: astore_2
41: aload_2
42: aload_1
43: invokestatic #6; //Method java/util/Collections.addAll:(Ljava/util/Collection;[Ljava/lang/Object;)Z
46: pop
47: 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的顺序问题(二)
更多推荐
所有评论(0)