为什么子列表中的更改会反映在原始列表中?

我知道当你通过引用传递它们时,Java中的Collections是可变的。
我想知道它的原始列表和子列表的内存地址究竟发生了什么。
子列表和原始列表是否引用相同的对象?

以下是代码示例,反映子列表中对主要原始列表所做的更改。

 List list = new ArrayList(); list.add("1"); list.add("2"); list.add(1, "3"); List list2 = new LinkedList(list); list.addAll(list2); list2 = list.subList(2, 5); list2.clear(); //Changes are made to list System.out.println(list); 

根据JavaDoc的问题:

列出subList(int fromIndex,int toIndex)

返回指定fromIndex(包含)和toIndex(独占)之间此列表部分的视图。 (如果fromIndex和toIndex相等,则返回的列表为空。) 返回的列表由此列表支持,因此返回列表中的非结构更改将反映在此列表中,反之亦然 。 返回的列表支持此列表支持的所有可选列表操作。

子列表将指向原始列表中存在的相同元素,因此,由于您要更改相同的对象,因此通过子列表所做的任何更改都将反映在原始列表中。

编辑:根据您的评论,假设original list具有以下参考: 0x00 0x01 0x02 0x03 0x04 0x05并且这些映射到存储器中存在对象的位置。

在上面执行sublist(0, 2)将产生一个列表,其中包含指向以下内存位置0x00 0x01 0x02指针,这些指针与original list中的相同。

这意味着如果你做sublist.get(0).setFoo(foo) ,这将依次寻找出现在0x00的对象并设置一些属性。 但是, original list也会引用0x00 ,这就是更改子列表意味着您将更改源列表的原因,因为两个列表都指向相同的对象 。 如果您通过original list更改元素,也同样适用。

检查此链接 。

SubList返回指定fromIndex(包括)和toIndex(不包括)之间的此列表部分的视图。 (如果fromIndex和toIndex相等,则返回的列表为空。)返回的列表由此列表支持,因此返回列表中的非结构更改将反映在此列表中,反之亦然。 返回的列表支持此列表支持的所有可选列表操作。

所以你的list2只是你的原始列表的子视图。这就是为什么当你清除list2时,你在原始列表中丢失了相应的值。检查这段代码。

 public static void main(String[] args) { List list = new ArrayList(); list.add("1"); list.add("2"); list.add(1, "3"); List list2 = new LinkedList(list); list.addAll(list2); System.out.println(list); list2 = list.subList(2, 5); System.out.println(list2); list2.clear(); //Changes are made to list1 System.out.println(list); } 

O / P

 [1, 3, 2, 1, 3, 2] [2, 1, 3] [1, 3, 2] 

排队

 list2 = list.subList(2, 5); 

你正在调用subList中引用的ArrayList subList方法。 它的代码看起来像这样

 public List subList(int fromIndex, int toIndex) { subListRangeCheck(fromIndex, toIndex, size); return new SubList(this, 0, fromIndex, toIndex); } 

因此在确认有效范围后,list2将存储结果

 new SubList(this, 0, fromIndex, toIndex); 

private class SubList extends AbstractList是在ArrayList定义的类,这个构造函数的代码如下所示

 SubList(AbstractList parent, int offset, int fromIndex, int toIndex) { this.parent = parent; this.parentOffset = fromIndex; this.offset = offset + fromIndex; this.size = toIndex - fromIndex; this.modCount = ArrayList.this.modCount; } 

所以它的parent字段将存储对原始ArrayList引用( new SubList( this , ...) )。

现在你打电话的时候

 list2.clear(); 

将调用由AbstractListSubListinheritance的clear()方法的代码

 public void clear() { removeRange(0, size()); } 

这将在内部调用在removeRange重写的SubList

 protected void removeRange(int fromIndex, int toIndex) { checkForComodification(); parent.removeRange(parentOffset + fromIndex, parentOffset + toIndex); this.modCount = parent.modCount; this.size -= toIndex - fromIndex; } 

如你所见,结果你正在打电话

 parent.removeRange(parentOffset + fromIndex, parentOffset + toIndex); 

在哪里,你记得parent持有对调用了subList ArrayList的引用。 因此,通过调用clear来有效地从您创建子列表的原始列表中调用removeRange