是否有一种干净(和安全的)方法来乘以Java中的地图值?

Oma*_*que 48 java java-8 java-stream

我有一个Map<String, Double>,并希望将地图中的所有值乘以2,比方说,但是将空值保持为空.

我显然可以使用for循环来做到这一点,但是想知道是否有更清洁的方法呢?

Map<String, Double> someMap = someMapFunction();
Map<String, Double> adjustedMap = new Hashmap<>();
if (someMap != null) {
    for (Map.Entry<String,Double> pair : someMap.entryset()) {
        if (pair.getValue() == null) {
            adjustedMap.put(pair.getKey(), pair.getValue());
        } else {
            adjustedMap.put(pair.getKey(), pair.getValue()*2)
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

有时候返回的地图someMapFunction是一个不可变的地图,所以这不能用就地做Map.replaceAll.我无法想出一个更清洁的流解决方案.

Era*_*ran 62

我的第一反应是提出一个Stream输入MapentrySet该值映射到新的价值,并与终止collectors.toMap().

不幸的是,值mapper函数返回时Collectors.toMap抛出.因此,它不适用于您输入的值.NullPointerExceptionnullnullMap

作为替代方案,因为您无法改变您的输入Map,我建议您创建它的副本然后调用replaceAll:

Map<String, Double> adjustedMap = new HashMap<>(someMap);
adjustedMap.replaceAll ((k,v) -> v != null ? 2*v : null);
Run Code Online (Sandbox Code Playgroud)

  • 一个类似的解决方案可以是`someMap.forEach((k,v) - > adjustedMap.put(k,v == null?null:2*v));`创建一个新的空映射后; 这样可以避免复制/替换地图中的值 (4认同)

Pet*_*ček 25

作为流式传输和/或复制解决方案的替代方案,Google Guava中Maps.transformValues()存在实用方法:

Map<String, Double> adjustedMap = Maps.transformValues(someMap, value -> (value != null) ? (2 * value) : null);
Run Code Online (Sandbox Code Playgroud)

这将返回原始地图的惰性视图,该视图不会自行执行任何工作,但会在需要时应用给定的函数.这既可以是一个亲(如果你不可能永远都需要所有的值,这会为你节省一些计算时间)和CON(如果你需要相同的值多次,或者如果你需要进一步修改someMap,而不adjustedMap看到变化)取决于您的使用情况.


Mar*_*o13 6

已有很多答案.其中一些似乎对我有点怀疑.在任何情况下,他们中的大多数都null以一种形式或另一种形式内联检查.

抽象阶梯向上迈出一步的方法如下:

您想要将一元运算符应用于地图的值.因此,您可以实现一个将一元运算符应用于映射值的方法.(到现在为止还挺好).现在,您需要一个"特殊"的一元运算符null-safe.然后,您可以null围绕原始运算符包装-safe一元运算符.

这里显示了三个不同的运算符(其中一个是运算符Math::sin):

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.UnaryOperator;

public class MapValueOps
{
    public static void main(String[] args)
    {
        Map<String, Double> map = new LinkedHashMap<String, Double>();
        map.put("A", 1.2);
        map.put("B", 2.3);
        map.put("C", null);
        map.put("D", 4.5);

        Map<String, Double> resultA = apply(map, nullSafe(d -> d * 2));
        System.out.println(resultA);

        Map<String, Double> resultB = apply(map, nullSafe(d -> d + 2));
        System.out.println(resultB);

        Map<String, Double> resultC = apply(map, nullSafe(Math::sin));
        System.out.println(resultC);

    }

    private static <T> UnaryOperator<T> nullSafe(UnaryOperator<T> op)
    {
        return t -> (t == null ? t : op.apply(t));
    }

    private static <K> Map<K, Double> apply(
        Map<K, Double> map, UnaryOperator<Double> op)
    {
        Map<K, Double> result = new LinkedHashMap<K, Double>();
        for (Entry<K, Double> entry : map.entrySet())
        {
            result.put(entry.getKey(), op.apply(entry.getValue()));
        }
        return result;
    }
}
Run Code Online (Sandbox Code Playgroud)

我认为这很干净,因为它很好地区分了应用运算符和执行null-check的问题.它是安全的null,因为...方法名称是这样说的.

(有人可能会说拉调用包裹运营商进入nullSafe一个apply方法,但在这里,这不是问题的关键)

编辑:

根据预期的应用程序模式,可以执行类似的操作并通过调用将转换应用到适当位置,而无需创建新映射Map#replaceAll


Lev*_*and 5

您可以通过转换为流来实现这一目标,例如:

someMap.entrySet()
        .forEach(entry -> {
            if (entry.getValue() != null) {
                adjustedMap.put(entry.getKey(), someMap.get(entry.getKey()) * 2);
            } else {
                adjustedMap.put(entry.getKey(), null);
            }
        });
Run Code Online (Sandbox Code Playgroud)

可以缩短为:

someMap.forEach((key, value) -> {
    if (value != null) {
        adjustedMap.put(key, value * 2);
    } else {
        adjustedMap.put(key, null);
    }
});
Run Code Online (Sandbox Code Playgroud)

所以,如果你有一张地图:

Map<String, Double> someMap = new HashMap<>();
someMap.put("test1", 1d);
someMap.put("test2", 2d);
someMap.put("test3", 3d);
someMap.put("testNull", null);
someMap.put("test4", 4d);
Run Code Online (Sandbox Code Playgroud)

你会得到这个输出:

{test4=8.0, test2=4.0, test3=6.0, testNull=null, test1=2.0}
Run Code Online (Sandbox Code Playgroud)

  • @PetrJaneček以及'forEach`无论如何都只能通过副作用起作用,这个答案确实*不*在任何地方使用`.stream()`这会产生很大的不同......但这可以[确实简化](https: //stackoverflow.com/a/52857133/1059372) (6认同)
  • 你的`someMap.get(key)`可以简单地说是`value`.无论如何,为风格downvoting.这可能是我个人的主观偏好,但我坚信功能代码应该是无副作用的.因此,在我的书中使用一个地图上的`forEach()`来更改不同的地图是禁忌.我更喜欢将流收集到新地图中.这是消费流的规范和公认的方式. (3认同)