use*_*533 41 java java-8 java-stream
我想要做的是在下面的2个流调用中显示.我想根据某些条件将一个集合拆分为两个新集合.理想情况下,我想在1中进行.我已经看到了用于.map函数的条件,但找不到forEach的任何内容.实现我想要的最好方法是什么?
animalMap.entrySet().stream()
.filter(pair-> pair.getValue() != null)
.forEach(pair-> myMap.put(pair.getKey(), pair.getValue()));
animalMap.entrySet().stream()
.filter(pair-> pair.getValue() == null)
.forEach(pair-> myList.add(pair.getKey()));
Run Code Online (Sandbox Code Playgroud)
Ale*_*rov 74
只需将条件放入lambda本身,例如
animalMap.entrySet().stream()
.forEach(
pair -> {
if (pair.getValue() != null) {
myMap.put(pair.getKey(), pair.getValue());
} else {
myList.add(pair.getKey());
}
}
);
Run Code Online (Sandbox Code Playgroud)
当然,这假设在上面的代码片段之前声明并初始化了两个集合(myMap和myList).
更新:使用Map.forEach使代码更短,加上更高效和可读性,正如Jorn Vernee所建议:
animalMap.forEach(
(key, value) -> {
if (value != null) {
myMap.put(key, value);
} else {
myList.add(key);
}
}
);
Run Code Online (Sandbox Code Playgroud)
Hol*_*ger 11
在大多数情况下,当您发现自己forEach在Stream上使用时,您应该重新考虑是否使用适合您工作的工具,或者您是否以正确的方式使用它.
通常,您应该寻找适合您想要实现的目标或适当的收集器的终端操作.现在,有一些收藏家用于制作Maps和Lists,但没有开箱即用的收藏家,根据谓词组合两个不同的收藏家.
现在,这个答案包含一个收集器,用于组合两个收集器.使用此收集器,您可以实现任务
Pair<Map<KeyType, Animal>, List<KeyType>> pair = animalMap.entrySet().stream()
.collect(conditional(entry -> entry.getValue() != null,
Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue),
Collectors.mapping(Map.Entry::getKey, Collectors.toList()) ));
Map<KeyType,Animal> myMap = pair.a;
List<KeyType> myList = pair.b;
Run Code Online (Sandbox Code Playgroud)
但也许,您可以通过更简单的方式解决此特定任务.其中一个结果与输入类型匹配; 它只是剥离了映射到的条目的相同地图null.如果您的原始地图是可变的并且之后不需要它,您可以只收集列表并从原始地图中删除这些键,因为它们是互斥的:
List<KeyType> myList=animalMap.entrySet().stream()
.filter(pair -> pair.getValue() == null)
.map(Map.Entry::getKey)
.collect(Collectors.toList());
animalMap.keySet().removeAll(myList);
Run Code Online (Sandbox Code Playgroud)
请注意,null即使没有其他键列表,您也可以删除映射:
animalMap.values().removeIf(Objects::isNull);
Run Code Online (Sandbox Code Playgroud)
要么
animalMap.values().removeAll(Collections.singleton(null));
Run Code Online (Sandbox Code Playgroud)
如果您不能(或不想)修改原始地图,则仍然存在没有自定义收集器的解决方案.正如Alexis C.的回答所示,partitioningBy正在朝着正确的方向发展,但你可以简化它:
Map<Boolean,Map<KeyType,Animal>> tmp = animalMap.entrySet().stream()
.collect(Collectors.partitioningBy(pair -> pair.getValue() != null,
Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)));
Map<KeyType,Animal> myMap = tmp.get(true);
List<KeyType> myList = new ArrayList<>(tmp.get(false).keySet());
Run Code Online (Sandbox Code Playgroud)
最重要的是,不要忘记普通的Collection操作,你不必使用新的Stream API做任何事情.
通过stream().forEach(..)调用add或put内部调用forEach(所以你改变外部myMap或myList实例)的问题是,如果有人并行转换流并且您正在修改的集合不是线程安全的,那么您可以轻松地运行并发问题.
您可以采取的一种方法是首先对原始地图中的条目进行分区.完成后,获取相应的条目列表并在相应的地图和列表中收集它们.
Map<Boolean, List<Map.Entry<K, V>>> partitions =
animalMap.entrySet()
.stream()
.collect(partitioningBy(e -> e.getValue() == null));
Map<K, V> myMap =
partitions.get(false)
.stream()
.collect(toMap(Map.Entry::getKey, Map.Entry::getValue));
List<K> myList =
partitions.get(true)
.stream()
.map(Map.Entry::getKey)
.collect(toList());
Run Code Online (Sandbox Code Playgroud)
...或者如果您想在一次传递中执行它,请实现自定义收集器(假设Tuple2<E1, E2>存在一个类,您可以创建自己的类),例如:
public static <K,V> Collector<Map.Entry<K, V>, ?, Tuple2<Map<K, V>, List<K>>> customCollector() {
return Collector.of(
() -> new Tuple2<>(new HashMap<>(), new ArrayList<>()),
(pair, entry) -> {
if(entry.getValue() == null) {
pair._2.add(entry.getKey());
} else {
pair._1.put(entry.getKey(), entry.getValue());
}
},
(p1, p2) -> {
p1._1.putAll(p2._1);
p1._2.addAll(p2._2);
return p1;
});
}
Run Code Online (Sandbox Code Playgroud)
用法:
Tuple2<Map<K, V>, List<K>> pair =
animalMap.entrySet().parallelStream().collect(customCollector());
Run Code Online (Sandbox Code Playgroud)
如果需要,可以更多地调整它,例如通过提供谓词作为参数.
我认为在Java 9中是可能的:
animalMap.entrySet().stream()
.forEach(
pair -> Optional.ofNullable(pair.getValue())
.ifPresentOrElse(v -> myMap.put(pair.getKey(), v), v -> myList.add(pair.getKey())))
);
Run Code Online (Sandbox Code Playgroud)
但是需要ifPresentOrElse使其起作用。(我认为for循环看起来更好。)
| 归档时间: |
|
| 查看次数: |
129909 次 |
| 最近记录: |