What is the difference between Collection<?> and Collection<T>
Asked Answered
I

5

25

I am mainly a C# developer and I was teaching Data Structures to my friend and they use Java in their University and I saw such an expression in Java:

void printCollection(Collection<?> c) {
    for (Object e : c) {
        System.out.println(e);
    }
}

I haven't seen such a thing in C# so I wonder what's the difference between Collection<T> and Collection<?> in Java?

void printCollection(Collection<T> c) {
    for (Object e : c) {
        System.out.println(e);
    }
}

I think it could have been written in the way above too. The guy in the documentation was comparing Collection<Object> and Collection<T> though.

Examples are taken from http://docs.oracle.com/javase/tutorial/extra/generics/wildcards.html

Isallobar answered 27/5, 2012 at 22:22 Comment(1)
Do you mean void printCollection(Collection<T> c) or <T> void printCollection(Collection<T> c)? The latter is a generic method. The former is not a generic method and must be declared in a generic class with a type parameter TDefeasible
L
29

Collection<?> is a collection of unknown type parameter.

As far as the caller is concerned, there is no difference between

void printCollection(Collection<?> c) { ... }

and

<T> void printCollection(Collection<T> c) { ... }

However, the latter allows the implementation to refer to the collection's type parameter and is therefore often preferred.

The former syntax exists because it is not always possible to introduce a type parameter at the proper scope. For instance, consider:

List<Set<?>> sets = new ArrayList<>();
sets.add(new HashSet<String>());
sets.add(new HashSet<Integer>());

If I were to replace ? by some type parameter T, all sets in sets would be restricted to the same component type, i.e. I can no longer put sets having different element types into the same list, as evidenced by the following attempt:

class C<T extends String> {
    List<Set<T>> sets = new ArrayList<>();

    public C() {
        sets.add(new HashSet<String>()); // does not compile
        sets.add(new HashSet<Integer>()); // does not compile
    }
}
Levison answered 27/5, 2012 at 23:7 Comment(1)
I am not sure I understood your last example. Could you please articulate it little bit more/Isallobar
H
19

The declaration Collection<?> (pronounced "collection of unknown") is a collection whose element type matches anything, whereas Collection<T> stands for a collection of type T.

As usual Angelika Langer's FAQ on generics has an extensive discussion on the topic, a must-read for fully understanding all about generics in Java, and unbounded wildcards (the subject of this question) in particular. Quoting from the FAQ:

The unbounded wildcard looks like " ? " and stands for the family of all types. The unbounded wildcard is used as argument for instantiations of generic types. The unbounded wildcard is useful in situations where no knowledge about the type argument of a parameterized type is needed

For further technical details, check out the section §4.5.1 Type Arguments and Wildcards of the Java Language Specification, which states that:

Type arguments may be either reference types or wildcards. Wildcards are useful in situations where only partial knowledge about the type parameter is required.

Hobard answered 27/5, 2012 at 22:25 Comment(2)
I wonder if there is a performance gain with one over another.Isallobar
None whatsoever, because of type erasure both forms are completely equivalent at runtime.Sthenic
G
5

With Collection<T> you could do

void printCollection(Collection<T> c) {
    for (T e : c) {
        System.out.println(e);
    }
}

With Collection<?> you only know the collection contains objects.

Greige answered 27/5, 2012 at 22:28 Comment(2)
so? with Collection<?> you could do void printCollection(Collection<?> c) { for (Object e : c) { System.out.println(e); } }Defeasible
Then why not Object since we need to use Object later in the method implementation?Isallobar
A
5

The one using the unbounded wildcard (?) actually means ? extends Object (anything that extends Object).

This, in Java, implies a read-only nature, namely, we are allowed to read items from the generic structure, but we are not allowed to put anything back in it, because we cannot be certain of the actual type of the elements in it.

Therefore, I daresay it is a very valid approach in the printCollection method under discussion, at least, until we meet a situation in which we need to assume a type.

If had to choose between them two, I would say that the second one (with type parameter T) is a cleaner approach because you can at least assume that the collection has a certain type T, and that can prove to be useful in certain scenarios.

For example, if the Collection needed to be synchronized, I could simply do:

<T> void printCollection(Collection<T> c) {
    List<T> copy = new ArrayList<T>();
    synchronized(c) {
        copy.addAll(c);
    }
    for (T e : copy) {
        System.out.println(e);
    }
}

I very easily created a new collection of type T, and copy all elements of the original collection into this second one. This I can do, because I can assume the type of the collection is T, and not ?.

I could, of course, do the same thing with the first method (using the ubounded wildcard), but it's not so clean, I had to make assumptions that the type of the Collection is Object, and not ? (which cannot be validly used as type argument: new ArrayList<?>()).

void printCollection(Collection<?> c) {
    List<Object> copy = new ArrayList<Object>();
    synchronized(c) {
        copy.addAll(c);
    }
    for (Object e : copy) {
        System.out.println(e);
    }
}
Algolagnia answered 27/5, 2012 at 23:52 Comment(0)
M
0

I very easily created a new collection of type T, and copy all elements of the original collection into this second one. This I can do, because I can assume the type of the collection is T, and not ?.

I could, of course, do the same thing with the first method (using the ubounded wildcard), but it's not so clean, I had to make assumptions that the type of the Collection is Object, and not ? (which cannot be validly used as type argument: new ArrayList<?>()).

Mellow answered 11/1, 2022 at 3:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.