Java 为什么子列表中的更改会反映在原始列表中?
我知道Java中的Java 为什么子列表中的更改会反映在原始列表中?,java,list,arraylist,sublist,Java,List,Arraylist,Sublist,我知道Java中的集合在通过引用传递时是可变的。 我想知道原始列表及其子列表的内存地址中到底发生了什么。 子列表和原始列表是否引用同一对象 下面是反映子列表对原始主列表所做更改的代码示例 List<String> list = new ArrayList<String>(); list.add("1"); list.add("2"); list.add(1, "3"); List<String> list2 = new LinkedList<Strin
集合
在通过引用传递时是可变的。我想知道原始列表及其子列表的内存地址中到底发生了什么。
子列表和原始列表是否引用同一对象 下面是反映子列表对原始主列表所做更改的代码示例
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);
List List=new ArrayList();
列表。添加(“1”);
列表。添加(“2”);
列表。添加(1,“3”);
List list2=新链接列表(List);
list.addAll(列表2);
list2=list.subList(2,5);
清单2.clear()//对列表进行了更改
系统输出打印项次(列表);
根据有关事项的说明:
列表子列表(int fromIndex,
int toIndex)
返回此列表中指定的fromIndex(包含fromIndex)和toIndex之间部分的视图,
独家(如果fromIndex和toIndex相等,则返回的列表为
空。)返回的列表由此列表支持,因此非结构化
返回列表中的更改将反映在此列表中,并且
反之亦然。返回的列表支持所有可选列表
此列表支持的操作
子列表将指向原始列表中存在的相同元素,因此,通过子列表所做的任何更改都将反映在原始列表中,因为您正在更改相同的对象
编辑:根据您的评论,假设原始列表
具有以下引用:0x00 0x01 0x02 0x03 0x04 0x05
,这些引用映射到内存中存在对象的位置
执行上面的子列表(0,2)
将生成一个列表,其中包含指向以下内存位置的指针0x00 0x01 0x02
,这些位置与原始列表中的相同
这意味着,如果执行sublist.get(0).setFoo(foo)
,这将依次查找位于0x00的对象并设置一些属性。但是,0x00
也被原始列表
引用,这就是为什么更改子列表意味着您将更改源列表,因为两个列表指向相同的对象。如果您通过原始列表
更改元素,也同样适用。请选中此项
子列表返回此列表中
从索引(包含)和索引(独占)指定。(如果来自索引)
和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);
}
一致
list2 = list.subList(2, 5);
您正在调用从列表
引用的数组列表
的子列表
方法。其代码如下所示
public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);
return new SubList(this, 0, fromIndex, toIndex);
}
其中,私有类子列表扩展了AbstractList
是在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;
}
将调用从AbstractList
继承的SubList
方法的clear()
代码
public void clear() {
removeRange(0, size());
}
它将在内部调用子列表中覆盖的删除范围
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
。以下是基于as添加到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);
子列表有一个对原始列表的引用,其偏移量用于知道子列表的开始位置,大小用于知道子列表的结束位置
对列表元素的操作将转发到原始列表
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);
请注意,通过将引用从索引5调整到索引2,并用空值填充数组的索引3、4和5,可以删除这些元素。内存中发生的事情是什么意思?就像任何引用同一事物的东西一样。没什么特别的here@ShubhamKharde当前位置我试图扩展我的答案。如果您仍然有问题,您将需要研究一般情况下引用是如何工作的。@Brandon Ling这些更改将如何反映?引用、子字符串和主列表必须指向同一个对象?或者子列表对象包含类似指针的内容,用于存储对应对象值所在的引用?小问题,sublist(0,2)
中的列表为什么包含4个元素?@phlaxyr:这是一个早就应该输入的错误。修好了,谢谢。人们只是不想深入讨论,如果他们的答案不被接受,他们就会投反对票。对于那些投反对票的人来说,这是我期待的答案,他们必须坚持被问到的问题。@ShubhamKharde人们通常不会因为他们的答案不被接受就投反对票,但如果他们认为这个问题不清楚,或者不包含一些证据或研究。老实说,我也不确定这是否是您正在寻找的答案,但因为我决定刷新我对收藏的记忆会很好,所以我决定做一些研究,并将它们作为答案发布。@ShubhamKharde看起来很相似,他使用内存地址描述代码,我在展示什么方法,什么时候,关于调用什么引用来显示要点,在结束调用中,list2.clear()
将导致从原始list
调用removeRange
。重点是sibList
不会创建将引用复制到原始列表中所有对象的列表,但只记住第一个和最后一个元素f的索引
parent.removeRange(parentOffset + fromIndex,
parentOffset + toIndex);
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();