我经常使用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();
}
}
我知道Apache Common集合DefaultedMap
,它可以在工厂/模型对象丢失时动态创建一个值;但你依靠(另一个)外部库只是为了避免编写2/3行代码的(相当小的)烦恼。
是否有更简单的解决方案(特别是例如#2)?在这种情况下,您的开发人员使用/推荐了什么?是否有其他图书馆提供这种&#34;默认地图&#34;?你写自己的装饰地图吗?
答案 0 :(得分:7)
在Java 8中,方法computeIfAbsent()
已添加到Map
接口:
default V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)
如果指定的键尚未与值关联(或映射为null),则尝试使用给定的映射函数计算其值,并将其输入此映射,除非为null。
根据documentation,最常见的用法是创建一个新对象作为初始映射值或实现多值映射。例如:
map.computeIfAbsent(key, k -> new HashSet<V>()).add(v);
所以你可以按如下方式重写你的例子:
/* 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());
另一种选择是将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()));
答案 1 :(得分:3)
Google的guava-libraries也提供了这样的地图实施。 但我不会仅仅为了这么小的利益而使用图书馆。当您已经使用这样的库时,您可以考虑使用这些地图。但总的来说,这只是我的意见,我不喜欢将库用于琐碎的事情。
您的问题中的示例对我来说似乎很好。我也在用这个成语。
答案 2 :(得分:1)
对我来说,它看起来像MultivaluedMap作为Java WS的一部分。
实施的相同所以为了回答你的问题,没有默认的实现,你必须自己动手或更好地使用其中一个实现。