我知道当你通过引用传递它们时,Java中的Collections
是可变的
我想确切地知道原始列表和子列表的内存地址中发生了什么
子列表和原始列表是否引用同一个对象?
以下是代码示例,反映子列表中对主要原始列表所做的更改。
List<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
list.add(1, "3");
List<String> list2 = new LinkedList<String>(list);
list.addAll(list2);
list2 = list.subList(2, 5);
list2.clear(); //Changes are made to list
System.out.println(list);
答案 0 :(得分:8)
根据问题的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
处的对象并设置一些属性。但是,0x00
也引用original list
,这就是为什么更改子列表意味着您将更改源列表,因为两个列表都指向相同的对象 。如果您通过original list
更改元素,也同样如此。
答案 1 :(得分:3)
这是基于java source code的代码示例存储器的简化可视化,是Pshemo的一个很好的答案:
List<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
list.add(1, "3");
List<String> list2 = new LinkedList<String>(list);
list.addAll(list2);
list2 = list.subList(2, 5);
SubList引用原始列表,并带有偏移量以了解子列表的起始位置,并提供一个大小来了解子列表的终止位置。
list2.clear();
对列表元素的操作将转发到原始列表。
请注意,通过从索引5到索引2处理引用并用空值填充数组的索引3、4和5来删除此元素。
答案 2 :(得分:2)
选中link。
SubList返回此列表之间的部分视图 指定fromIndex,包含和toIndex,独占。 (如果是fromIndex 和toIndex相等,返回的列表为空。)返回的列表 由此列表支持,因此返回列表中的非结构更改 反映在此列表中,反之亦然。返回的列表支持 此列表支持的所有可选列表操作。
所以你的list2只是你的原始列表的子视图。这就是为什么当你清除list2时,你在原始列表中丢失了相应的值。检查这段代码。
public static void main(String[] args)
{
List<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
list.add(1, "3");
List<String> list2 = new LinkedList<String>(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]
答案 3 :(得分:1)
排队
list2 = list.subList(2, 5);
您正在调用从subList
引用的ArrayList
list
方法。它的代码看起来像这样
public List<E> 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<E>
是ArrayList
中定义的类,此构造函数的代码如下所示
SubList(AbstractList<E> 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();
来自clear()
的{{1}}继承的SubList
方法的代码将被调用
AbstractList
将在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
的ArrayList的引用。通过调用subList
,您可以从您创建子列表的原始列表中调用clear
。