使用Java在地图中自动创建缺失值的习惯用法

Lau*_*ire 3 java collections idioms

我经常使用map来存储循环中的值,例如属于同一个类/组的对象的set/list,或者我想要递增的AtomicInteger.因此,我经常编写以下类型的代码(假设我不在地图中存储null):

/* Example #1 -- aggregation */
Map<K, Set<O>> map = new HashMap<K, Set<O>>();
for (O o : oList) {
    K k = o.getK();
    Set<O> oSet = map.get(k);
    if (oSet == null) {
        oSet = new HashSet<O>(o);
        map.put(k, oSet);
    } else {
        oSet.add(o);
    }
}

/* Example #2 -- counting */
Map<K, AtomicInteger> map = new HashMap<K, AtomicInteger>();
for (O o : oList) {
    K k = o.getK();
    AtomicInteger i = map.get(k);
    if (i == null) {
        i = new AtomicInteger(1);
        map.put(k, i);
    } else {
        i.increment();
    }
}
Run Code Online (Sandbox Code Playgroud)

我知道Apache Common集合DefaultedMap可以在工厂/模型对象丢失时动态创建一个值; 但你依靠(另一个)外部库只是为了避免编写2/3行代码的(相当小的)烦恼.

是否有更简单的解决方案(特别是例如#2)?在这种情况下,您的开发人员使用/推荐了什么?还有其他图书馆提供这种"默认地图"吗?你写自己的装饰地图吗?

And*_*ira 7

在Java 8中,该方法computeIfAbsent()被添加到Map接口:

default V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)

如果指定的键尚未与值关联(或映射为null),则尝试使用给定的映射函数计算其值,并将其输入此映射,除非为null.

根据文档,最常见的用法是创建一个新对象作为初始映射值或实现多值映射.例如:

map.computeIfAbsent(key, k -> new HashSet<V>()).add(v);
Run Code Online (Sandbox Code Playgroud)

所以你可以按如下方式重写你的例子:

/* Example #1 -- aggregation */
Map<K, Set<O>> map = new HashMap<>();
oList.forEach(o -> map.computeIfAbsent(o.getK(), k -> new HashSet<>()).add(o));

/* Example #2 -- counting */
Map<K, AtomicInteger> map = new HashMap<>();
oList.forEach(o -> map.computeIfAbsent(o.getK(), k -> new AtomicInteger(0)).incrementAndGet());
Run Code Online (Sandbox Code Playgroud)

另一个选择是使用Stream API以及Collectors.groupingBy:

/* Example #1 -- aggregation */
Map<K, Set<O>> map = oList.stream()
                          .collect(Collectors.groupingBy(O::getK, Collectors.toSet()));

/* Example #2 -- counting using a Long instead of an AtomicInteger */
Map<K, Long> map = oList.stream()
                        .map(O::getK)
                        .collect(Collectors.groupingBy(k -> k, Collectors.counting()));
Run Code Online (Sandbox Code Playgroud)