Java 8中是否有更简洁的方法来处理Map操作?

3

在集合映射中,非常常见的操作是创建一个新的集合,并在键不存在时设置初始值,或者如果键存在,则对现有集合执行某些函数。例如,考虑一个 Map<String, Set<Integer>>,如果键不存在,则创建一个带有初始值为1的Set。如果键存在,则将值map.size()+1添加到集合中(或将此函数替换为其他简单的一行操作)。在Java 7中,使用if/else很简单,但代码比较冗长。我只能想出下面的Java 8代码,它并不比之前更好(实际上由于代码更多而更差)。有没有办法使这个代码更加简洁?

public void process(Map<String, Set<Integer>> m, String key) {
    m.compute(key, (k, v) -> {
        if (v == null) {
            v = new HashSet<>();
            v.add(1);
            return v;
        } else {
            v.add(v.size() + 1);
            return v;
        }
    });
}

这只是一个编程中的虚构例子吗?为什么要保留一组连续的整数?存储 [1, 2, 3, 4, 5] 没有意义,因为你可以只存储数字 5。 - Michael
如果你从1开始递增大小,它看起来非常像你只是在计数 - 是这样吗? - Eugene
1
我假设(也希望)这只是一个简化的例子。 - Thilo
1
这只是一个简化的例子。您可以用其他简单的一行代码替换v.add(v.size() + 1)。 - BGRT
3个回答

4

这里提供另一种选项:

Set<Integer> set = m.computeIfAbsent (key , k -> new HashSet<> ());
set.add(set.size() + 1);

唯一的原因是需要获取 Set 的当前大小,以决定要添加哪个值,这就是为什么它需要两行代码(而不是一个)。

到目前为止最好的 - Thilo

3

很遗憾,这不是一个一行代码的解决方案,但它可以实现其功能并且更易读(缺点:每次都会创建一个新的HashSet<>())。

m.putIfAbsent(key, new HashSet<>());
// Solution 1 :
m.compute(key, (k, v) -> {v.add(v.size() + 1); return v;});
// Solution 2 :
Set<Integer> s = m.get(key);
s.add(s.size() + 1);

或者像@Thilo提出的那样,受@Eran的启发

m.computeIfAbsent(key, k -> new HashSet<>()).add(m.get(key).size() + 1);

这个一行代码的可能性是因为它返回了它计算的值,就像在javadoc中提到的那样。

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

甚至在javadoc中也有一个类似的例子。

map.computeIfAbsent(key, k -> new HashSet()).add(v);

这个一行代码的小折衷是多余的调用m.get(key),而在@Eran的解决方案中没有发生。


1
如果你想避免创建所有这些不需要的 HashSet,可以使用 computeIfAbsent - Thilo
确实,然后我们就会有一个一行代码 :) - Yassin Hajaj
1
第二个很好。我没有意识到computeIfAbsent()返回函数的结果。 - BGRT
@BGRT 然而,这会增加在 Map (m.get(key)) 中运行另一个查找的(小)成本,如果您只使用两行代码就可以避免这种情况。 - Eran
@Eran 确实,我会添加它,并添加有关返回值的Javadoc。 - Yassin Hajaj

0
Set<Integer> v = m.getOrDefault(key, new HashSet<>());
v.add(v.size() + 1);
m.put(key, v);

虽然这个方法可行,但是这个答案中的解决方案更好。 - Mark Rotteveel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接