在检查 ArrayList API 时,我注意到一些看起来很奇怪的东西。
事实上,这里的 ArrayList 构造函数实现与一个集合作为参数传递:
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
size = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
这里等效于哈希集类:
public HashSet(Collection<? extends E> c) {
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
因此,我们可以注意到 ArrayList 一个使用了参数集合提供的元素的 COPY (Arrays.copyOf),而 HashSet 一个使用 addAll() 方法。
当然,addAll() 方法不会复制元素,而只是添加对 HashSet 集合的引用。
我发现这种微妙的差异对于忽略它的呼叫者来说是“危险的”。
人们可以期待一个具有相同引用的集合,另一个很好地阅读 ArrayList API 的人会期望来自原始集合的元素副本。
为什么 Sun 没有为这些集合子类提供相同的概念?
ArrayList one 使用 COPY (Arrays.copyOf) 的元素 in parameter ,而 HashSet 使用 addAll() 方法。
不,Arrays.copyOf
只复制数组,而不复制此数组指向的对象。不会克隆对象。话虽如此,这两个构造函数的行为相同 - 它们将包含对与原始集合相同的对象的引用。修改一个集合中的对象将在另一个集合中修改它(因为它是同一个对象)。
另请注意,Arrays.copyOf()
仅在某些情况下使用。
ArrayList
和 HashSet
将仅复制引用,而不复制这些引用引用的实际对象。
在 Java 中,非基元类型的变量是对对象的引用。如果你有一个这样的数组,那么 Arrays.copyOf
只复制引用 - 而不是这些引用引用的对象。
这些集合将具有相同的引用。如果您有一个包含三个对象 A、B、C 的列表并复制该列表,则新副本也将引用这相同的 3 个对象。这些构造函数都是浅层的,它们根本不接触原始对象。
public static void main(String args[]) {
ArrayList l = new ArrayList();
Object a = new Object();
Object b = new Object();
Object c = new Object();
l.add(a);
l.add(b);
l.add(c);
ArrayList k = new ArrayList(l);
HashSet h = new HashSet(l);
System.out.println(a);
System.out.println(b);
System.out.println(c);
System.out.println(l);
System.out.println(k);
System.out.println(h);
}
给:
java.lang.Object@43256ea2
java.lang.Object@4e82701e
java.lang.Object@558ee9d6
[java.lang.Object@43256ea2, java.lang.Object@4e82701e, java.lang.Object@558ee9d6]
[java.lang.Object@43256ea2, java.lang.Object@4e82701e, java.lang.Object@558ee9d6]
[java.lang.Object@4e82701e, java.lang.Object@43256ea2, java.lang.Object@558ee9d6]
您会注意到所有集合都引用相同的对象。(哈希集的顺序不同。