Реализация Java Map, которая возвращает значение по умолчанию вместо null

У меня есть Map<String, List<String>> в моем коде, где я бы избегал потенциальных нулевых указателей, если метод map #get() возвращал пустой список вместо null. В java API есть что-то подобное? Должен ли я просто расширить HashMap?

Ответ 1

@Ответ на вопрос - это хороший способ сделать то, что вы задаете напрямую.

Но мне кажется, что то, что вы пытаетесь реализовать, - это "multimap"; то есть отображение от ключа к набору значений. Если это так, то вы также должны посмотреть на классы мультимапов в коллекциях коллекций Guava или Apache.

Посмотрите:

Ответ 2

Как отмечено в комментариях:

Концепция компьютерной карты Guava была заменена на LoadingCache. Также java 8 представил интерфейс карты nice computeIfAbsent метод по умолчанию, который не нарушает договор карты и не дает ленивой оценки.


Guava имел идею "вычислительной карты", которая будет выполнять функцию, чтобы обеспечить значение, если оно не присутствует. Он был реализован в MapMaker.makeComputingMap; теперь вы можете использовать CacheBuilder - подробнее CacheBuilder.build.

Это может быть излишним для того, что вам нужно - вам может быть лучше просто написать реализацию Map, которая имеет Map (используя композицию, а не расширение какой-либо конкретной реализации), а затем просто возвращает значение по умолчанию, если оно нет. Любой метод, отличный от get, возможно, просто делегирует другую карту:

public class DefaultingMap<K, V> implements Map<K, V>
{
    private final Map<K, V> map;
    private final V defaultValue;

    public DefaultingMap(Map<K, V> map, V defaultValue)
    {
        this.map = map;
        this.defaultValue = defaultValue;
    }

    @Override public V get(Object key)
    {
        V ret = map.get(key);
        if (ret == null)
        {
            ret = defaultValue;
        }
        return ret;
    }

    @Override public int size()
    {
        return map.size();
    }

    // etc
}

Ответ 3

Благодаря методам default Java 8 теперь имеет встроенную систему с Map::getOrDefault:

Map<Integer, String> map = ...
map.put(1, "1");
System.out.println(map.getOrDefault(1, "2")); // "1"
System.out.println(map.getOrDefault(2, "2")); // "2"

Ответ 4

Как и в предыдущих сообщениях, вы можете просто переопределить метод get, если хотите изменить его поведение.

Map<String, List<String>> map = new LinkedHashMap<String, List<String>>() {
    public String get(Object key) {
        List<String> list = super.get(key);
        if (list == null && key instanceof String)
           super.put(key, list = new ArrayList<String>());
        return list;
    }
}; 

Ответ 5

В Guava есть способ сделать именно то, что вы хотите. Это похоже на Арготе ответ.

Map<String, List<String>> myMap = ...
Functions.forMap(myMap, Arrays.asList("default", "value"));

Ответ 6

Мне кажется, что вы можете просто написать функцию-оболочку, чтобы получить нужное значение, и если она null возвращает значение по умолчанию.

Map<String, List<String>> myMap = new Map<String, List<String>>();

public List<String> myGet(String key) {
    List<String> ret = myMap.get(key);
    if(ret == NULL) {
        return defaultList(); // where defaultList is a function that generates your default list.
    }
    return ret;
}

Это предполагает, что ваш myMap является глобальной переменной (для простоты), если это не то, что вы хотите, вы также можете расширить Map или HashMap во что-то вроде myMap и просто включить эту дополнительную функцию, Таким образом, вы можете использовать его из любого места, где создается объект myMap.