在检查ArrayList API时,我注意到一些看起来很奇怪的东西。
实际上,这里是一个ArrayList构造函数实现,其Collection作为参数传递:
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);
}
这里等同于HashSet类:
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集合的引用。
对于忽略它的调用者,我发现这个subtil的区别是“危险的”。
人们可以期待一个带有SAME引用的集合,另一个读取ArrayList API的人会期望从原始集合中复制元素。
为什么Sun没有为那些Collections子类提供相同的概念?
答案 0 :(得分:8)
ArrayList一个使用了参数集合提供的COPY(Arrays.copyOf)元素,而HashSet一个,使用addAll()方法。
不,Arrays.copyOf
仅复制数组,但不复制此数组指向的对象。没有克隆对象。也就是说,两个构造函数的行为相同 - 它们将引用包含在与原始集合相同的对象中。修改一个集合中的对象将在另一个集合中修改它(因为它是同一个对象)。
另请注意,Arrays.copyOf()
仅在某些情况下使用。
答案 1 :(得分:3)
ArrayList
和HashSet
都只会复制引用,而不会复制这些引用所引用的实际对象。
在Java中,非基本类型的变量是对对象的引用。如果你有一个数组,那么Arrays.copyOf
只复制引用 - 而不是那些引用所引用的对象。
答案 2 :(得分:1)
集合将具有相同的引用。如果您有一个包含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]
您会注意到所有集合都引用了相同的对象。 (HashSet的顺序不同。)