首页 > 解决方案 > java在处理ArrayLists时如何处理对象引用?

问题描述

在编写一段代码时,我观察到一个不寻常的行为。有一个类对象,它具有另一个名为 as的obj1类对象的数组列表。请参阅代码以供参考:obj2list1

PriorityQueue<Obj2> pq  = new PriorityQueue<>(Comparator);
  pq.addAll(new ArrayList<>(obj1.getList()));
  Obj2 obj2 = pq.poll();
  obj2.setField("any value");
  System.out.println(obj2); 
  System.out.println(obj1.getList().get(0));

上面的两个 sout 语句都打印了该值。
为什么会这样?我在 pq 中更改了 obj2 引用的值,而不是在 Obj1 本身中

在向 中添加元素时pq,如果我们不使用,new ArrayList<>()那么如果两个引用都指向同一个对象是可以理解的,但是我创建了一个新的 ArrayList 来添加pq,仍然发生这种情况。

标签: javaobjectarraylistpriority-queue

解决方案


JavaDocArrayList(Collection<? extends E> c)说:_

按照集合的迭代器返回的顺序构造一个包含指定集合元素的列表。

它没有说“复制”。

Java 中也没有“深拷贝”机制。即使您使用Object.clone(通常不应该这样做),您也只会复制此对象内的引用。引用本身仍将指向原始内容。

例如:

class Obj {
  String a;
  int b;
  OtherObj c;
}

在内存中,这将如下所示:

[Reference To String a] [Value of int b] [Reference to OtherObj c]

(只有原始类型将直接存储在对象中,其他所有内容都是 aClass并将作为引用存储。即使是像这样的原始包装器Integer也是类并将作为引用存储,但是这些原始包装器是不可变的,尽管你不能改变他们的内在价值)

虽然如果您创建此对象的副本,您将获得副本的新内存位置,但该内存位置将包含相同的数据:[Reference To String a] [Value of int b] [Reference to OtherObj c].

同样的情况也发生在ArrayList. 在内存中它看起来像这样:

[Reference to Element 1] [Reference to Element 2] [Reference to Element 3] ...

如果您复制该列表,您将在内存中获得该部分的副本。但是所有的引用仍然指向同一个对象。

这一切都可能随着 Project Valhalla 和 Value 类型的引入而改变。但这可能仍需要数月或数年。


推荐阅读