我有一个我想要转换和过滤的Java Map.作为一个简单的例子,假设我想将所有值转换为整数然后删除奇数条目.
Mapinput = new HashMap<>(); input.put("a", "1234"); input.put("b", "2345"); input.put("c", "3456"); input.put("d", "4567"); Map output = input.entrySet().stream() .collect(Collectors.toMap( Map.Entry::getKey, e -> Integer.parseInt(e.getValue()) )) .entrySet().stream() .filter(e -> e.getValue() % 2 == 0) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); System.out.println(output.toString());
这是正确的,并产生: {a=1234, c=3456}
但是,我不禁想知道是否有办法避免.entrySet().stream()
两次打电话.
有没有办法可以执行转换和过滤操作,.collect()
最后只调用 一次?
是的,您可以将每个条目映射到另一个临时条目,该条目将保存密钥和解析的整数值.然后,您可以根据其值过滤每个条目.
Mapoutput = input.entrySet() .stream() .map(e -> new AbstractMap.SimpleEntry<>(e.getKey(), Integer.valueOf(e.getValue()))) .filter(e -> e.getValue() % 2 == 0) .collect(Collectors.toMap( Map.Entry::getKey, Map.Entry::getValue ));
请注意,我使用Integer.valueOf
而不是parseInt
因为我们实际上想要一个盒装int
.
如果您有幸使用StreamEx库,您可以非常简单地完成:
Mapoutput = EntryStream.of(input).mapValues(Integer::valueOf).filterValues(v -> v % 2 == 0).toMap();
以较小的开销解决问题的一种方法是将映射和过滤向下移动到收集器.
Mapoutput = input.entrySet().stream().collect( HashMap::new, (map,e)->{ int i=Integer.parseInt(e.getValue()); if(i%2==0) map.put(e.getKey(), i); }, Map::putAll);
这不需要创建中间Map.Entry
实例,甚至更好,将int
值的装箱推迟到实际添加到值时的值Map
,这意味着过滤器拒绝的值根本没有装箱.
与此相比Collectors.toMap(…)
,操作也通过使用Map.put
而不是Map.merge
事先知道我们不必处理关键冲突来简化操作.
但是,只要您不想使用并行执行,您也可以考虑普通循环
HashMapoutput=new HashMap<>(); for(Map.Entry e: input.entrySet()) { int i = Integer.parseInt(e.getValue()); if(i%2==0) output.put(e.getKey(), i); }
或内部迭代变体:
HashMapoutput=new HashMap<>(); input.forEach((k,v)->{ int i = Integer.parseInt(v); if(i%2==0) output.put(k, i); });
后者非常紧凑,至少与所有其他有关单螺纹性能的变体相同.
番石榴是您的朋友:
Mapoutput = Maps.filterValues(Maps.transformValues(input, Integer::valueOf), i -> i % 2 == 0);
请记住,output
是一个转变,过滤视图中input
。如果要独立操作它们,则需要进行复制。