ytoledano
ytoledano

Reputation: 3123

Java: add to map of collections

I'm trying to write a generic function that will add an element to a Map of Collections. This works for a Map of Lists:

public static <TKey, TVal> void addToMapOfLists(Map<TKey, List<TVal>> map, TKey key, TVal val) {
    List<TVal> list = map.get(key);
    if (list == null) {
        list = new ArrayList<>();
        list.add(val);
        map.put(key, list);
    } else
        list.add(val);
}

I want to make this function work on Map<TKey, Set<TVal>> as well as on Map<TKey, List<TVal>>. I expect this should be possible because both implement Collection that has the add(TVal) member that I call.

My problem is that when I try change the parameter Map<TKey, List<TVal>> map to Map<TKey, ? extends Collection<TVal>> map - I need to somehow replace new ArrayList<>(); with a call to the constructor of the implementer of Collection.

Upvotes: 2

Views: 2455

Answers (1)

Eran
Eran

Reputation: 394126

You will have to pass an additional parameter to your method - a Supplier of Collection instances.

Here's one possible implementation :

public static <TKey, TVal, TCol extends Collection<TVal>> void addToMapOfCollections(Map<TKey, Collection<TVal>> map, TKey key, TVal val, Supplier<TCol> supplier) 
{
    Collection<TVal> col = map.get(key);
    if (col == null) {
        col = supplier.get ();
        col.add(val);
        map.put(key, col);
    } else {
        col.add(val);
    }
}

Here's another option :

public static <TKey, TVal> void addToMapOfCollections(Map<TKey, Collection<TVal>> map, TKey key, TVal val, Supplier<Collection<TVal>> supplier)
{
    Collection<TVal> col = map.get(key);
    if (col == null) {
        col = supplier.get ();
        col.add(val);
        map.put(key, col);
    } else {
        col.add(val);
    }
}

And with less code (as suggested by Gerald) :

public static <TKey, TVal> void addToMapOfCollections(Map<TKey, Collection<TVal>> map, TKey key, TVal val, Supplier<Collection<TVal>> supplier)
{
    map.putIfAbsent(key, supplier.get());
    map.get(key).add(val);
}

I tested the second variant :

Map<String,Collection<Integer>> map = new HashMap<String, Collection<Integer>>();
addToMapOfCollections(map,"String1",5,HashSet::new);
addToMapOfCollections(map,"String2",67,ArrayList::new);
addToMapOfCollections(map,"String2",68,ArrayList::new);
System.out.println (map);
for (Collection<Integer> col : map.values ()) {
    System.out.println (col.getClass () + " : " + col);
}

Output :

{String2=[67, 68], String1=[5]}
class java.util.ArrayList : [67, 68]
class java.util.HashSet : [5]

Upvotes: 7

Related Questions