Mik378
Mik378

Reputation: 22171

ArrayList(Collection c) VS HashSet(Collection c)

While inspecting ArrayList API, I noticed something that looks strange.

Indeed, here the the ArrayList constructor implementation with a Collection passed as argument :

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

and here the equivalent into the HashSet Class :

public HashSet(Collection<? extends E> c) {
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        addAll(c);
}

So, we can notice that the ArrayList one used a COPY (Arrays.copyOf) of elements provided by the collection in parameter whereas the HashSet one, use addAll() method.

And of course, addAll() method doesn't copy elements but just add references to the HashSet collection.

I find this subtil difference "dangerous" for caller that ignores it.

One could expect a collection with SAME references, another one who read ArrayList API well would expect a COPY of elements from original Collection.

Why didn't Sun provide same concept for those Collections subclasses ?

Upvotes: 2

Views: 1003

Answers (3)

Will Hartung
Will Hartung

Reputation: 118593

The collections will have the same references. If you have a list of three objects, A, B, C, and copy that list, the new copy will also reference those identical 3 objects. These constructors are both shallow, they don't touch the original objects at all.

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

Gives:

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]

You'll notice that all of the collections reference the identical objects. (The HashSet is in a different order.)

Upvotes: 1

Jesper
Jesper

Reputation: 206776

Both ArrayList and HashSet will copy only the references, not the actual objects that those references refer to.

In Java, a variable of a non-primitive type is a reference to an object. If you have an array of those, then Arrays.copyOf copies only the references - not the objects that those references refer to.

Upvotes: 3

Tomasz Nurkiewicz
Tomasz Nurkiewicz

Reputation: 340708

ArrayList one used a COPY (Arrays.copyOf) of elements provided by the collection in parameter whereas the HashSet one, use addAll() method.

No, Arrays.copyOf only copies the array, but not objects this array points to. The objects aren't cloned. That being said both constructors behave the same - they will contain references to the same objects as the original collection. Modifying an object in one collection will modify it in the other (because it is the same object).

Also note that Arrays.copyOf() is only used in some circumstances.

Upvotes: 8

Related Questions