为什么子列表中的更改会反映在原始列表中?
我知道当你通过引用传递它们时,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();
将调用由AbstractList
的SubList
inheritance的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
。