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

我知道Java中的
集合
在通过引用传递时是可变的。
我想知道原始列表及其子列表的内存地址中到底发生了什么。
子列表和原始列表是否引用同一对象

下面是反映子列表对原始主列表所做更改的代码示例

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();