I wrote this handy, generic function for converting a collection of collections into a single set:
public static <T> Set<T> makeSet(Collection<Collection<T>> a_collection) {
Iterator<Collection<T>> it = a_collection.iterator();
Set<T> result = new HashSet<T>();
while (it.hasNext()) {
result.addAll(it.next());
}
return result;
}
Then I tried to call it:
List<List<String>> resultLists = ... ;
Set<String> labelsSet = CollectionsHelper.makeSet(resultLists);
and I received the following error:
<T>makeSet(java.util.Collection<java.util.Collection<T>>) in CollectionsHelper
cannot be applied to (java.util.List<java.util.List<java.lang.String>>)
Now a List
is a Collection
, and a String
is a T
. So why doesn't this work and how do I fix it?
A List is an ordered Collection (sometimes called a sequence). Lists may contain duplicate elements. In addition to the operations inherited from Collection , the List interface includes operations for the following: Positional access — manipulates elements based on their numerical position in the list.
Adding an element to a Collection is done via the add() method. Here is an example of adding an element to a Java Collection : String anElement = "an element"; Collection collection = new HashSet(); boolean didCollectionChange = collection. add(anElement);
Typecasting to list can be done by simply using list(set_name) . Using sorted() function will convert the set into list in a defined order.
Any Collection can be passed as an argument to the constructor as long as its type extends the type of the ArrayList , as String extends Object . The constructor takes a Collection , but List is a subinterface of Collection , so you can just use the List<String> .
Your signature should be:
public static <T> Set<T> makeSet(Collection<? extends Collection<T>> coll);
Basically List<S>
is not a subtype of List<T>
just because S
is a subtype of T
. That property is called covariance and, in Java, generic types are not covariant (other languages such as scala contain covariant generic types).
What you did didn't work because it should be possible to add any Collection<T>
into a Collection<Collection<T>>
, so for example, with your signature, this would be a valid implementation:
public static <T> Set<T> makeSet(Collection<Collection<T>> coll) {
coll.add(new HashSet<T>());
return null;
}
But then calling this method as follows:
List<List<String>> outside = new LinkedList<List<String>>();
makeSet(outside); //actually this line will not compile!
List<String> oops = outside.get(0); //oh dear - it's a HashSet
So does this lead to the same problem? NO! The reason being that the compiler will not let you add anything into a collection parameterized on an unknown type:
public static <T> Set<T> makeSet(Collection<? extends Collection<T>> coll) {
coll.add(new HashSet<T>()); //this line will not compile
return null;
}
Having wildcards was necessary in the first place so that you could do things like what you wanted to do, probably best demonstrated by how the Collection.addAll
method was generified so that List<Number>.addAll(List<Integer>)
would be allowed:
boolean addAll(Collection<? extends T> coll)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With