为什么修改副本后,一个ArrayList也会改变

5

这可能是一个非常简单的问题,但它仍然让我感到困惑!

import java.util.ArrayList;

public class Sample {
    ArrayList<Integer> i = new ArrayList<>();
    ArrayList<Integer> j = new ArrayList<>();

    /**
     * @param args
     */
    public static void main(String[] args) {
        new Sample().go();
    }

    private void go() {

        i.add(1);
        i.add(2);
        i.add(3);

        j=i;

        i.remove(0);


        System.out.println(i + "asd" + j);
    }

}

我尝试打印它:

[2, 3]asd[2, 3]

为什么i变化时j也会改变呢?但原始数据类型不会出现这种情况!


1
你应该搜索Java如何处理内存,特别是对象引用以及浅拷贝和深拷贝之间的区别。 - telkins
6个回答

10

语句j=i;会将引用j指向和i相同的引用。现在,ij都指向同一个ArrayList对象。删除0号索引对于两个引用都是可见的。

如果您不希望从j访问的列表受到i中项目的影响,则应创建列表副本,而不是分配引用:

j = new ArrayList<Integer>(i);

这是一个浅拷贝,因此列表仍然引用相同的元素。


实际上,这对所有可变对象都发生。 - Zong
在Java中,除了原始类型之外,所有东西都是按引用传递的。(我纠正了一下 :)) - meskobalazs
3
Java中所有东西都是按值传递的,对于对象来说,引用也是按值传递的。 - telkins
@meskobalazs 请查看Java是“按引用传递”吗? - rgettman
是的,我应该更加具体明确。 - meskobalazs

1

Use

j = new ArrayList<>(i);

or

Collections.copy(j, i);

创建一个副本。

使用j = i只会使j指向i(这被称为引用)。这适用于涉及对象的所有赋值操作(不包括像intfloat这样的原始类型)。


1
对象并没有被克隆,只是增加了一个额外的对象引用。由于ArrayList不是不可变的,因此对对象的任何更改都会反映在两个对象引用中。

0
让我为您以以下方式处理:
ArrayList<Integer> i = new ArrayList<>();
ArrayList<Integer> j = new ArrayList<>();

// checking hash code before j = i;
System.out.println(System.identityHashCode(i));
System.out.println(System.identityHashCode(j));

j = i;

// checking hash code after j = i;
System.out.println(System.identityHashCode(i));
System.out.println(System.identityHashCode(j));

比较这两个值,如果它们相同,那么在 j=i; 之后,ArrayList j 现在指向 ArrayList i

在我的机器上输出结果为:

30269696 //hashCode of i
24052850 //hashCode of j before j = i;

30269696 //hashCode of i and j are same after j = i that means they are pointing to same reference and hence change in one reflects on the other.
30269696

0
对象和基本类型的工作方式有所不同。将对象 i 视为该对象的名称。当您说j=i时,您告诉JVM“忘记我说过另一个名为jArrayList; 从现在开始,当我提到j时,我指的是这个也可以称为iArrayList。”实际上,这正是发生的事情:在那一行之后,两个变量都引用同一个对象。
基本类型按您所说的方式工作。如果您说i=5; j=i; i=6,那么j仍将设置为5。

0
你为 j 创建了一个内存阶段,使用 ; j = new ArrayList<>();
但是之后你又说让 j 引用 i 的内存阶段。所以在 j=i; 之后,对 i 或 j 的任何更改都会影响它们两个。因为它们引用相同的对象。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接