Search code examples
javalistdictionarycollectionsguava

Convenience class for handling Map<K, List<V>>


On various projects I worked on, I found a pattern like

Map<Integer, List<String>> listMap = new HashMap<Integer, List<String>>();

public void addValue(Integer key, String value) {
    if (!listMap.containsKey(key)) {
        listMap.put(key, new ArrayList<String>());
    }
    listMap.get(key).add(value);
}

to populate Maps that have Lists as values.

As this is such a common pattern I wondered if there isn't some convencience class in the standard libraries or something like guava that manages adding and removing values to that structure.


Solution

  • Guava offers Multimap, which is very suited for your purpose:

    ListMultimap<Integer, String> listMap = ArrayListMultimap.create();
    
    public void addValue(Integer key, String value) {
        listMap.put(key, value);
    }
    
    public List<String> getValues(Integer key) {
        return listMap.get(key);
    }
    
    public Map<Integer, List<String>> asMap() {
        // See documentation for details
        return Multimaps.asMap(listMap);
    }
    

    Java 8 also offers a more convenient way of getting and initializing a value in a single statement:

    Map<Integer, List<String>> listMap = new HashMap<>();
    
    public void addValue(Integer key, String value) {
        listMap.computeIfAbsent(key, k -> new ArrayList<>()).add(value);
    }
    

    On a side note, I would modify your original example a bit to avoid an extra lookup when a matching key is found:

    public void addValue(Integer key, String value) {
        List<String> values = listMap.get(key);
        if (values == null) {
            listMap.put(key, values = new ArrayList<>());
        }
        values.add(value);
    }