问题描述
我有一个测试程序:
public class App {
public static void main(String[] args) {
List<Integer> a = Arrays.asList(1,11);
List<Integer> b = Arrays.asList(2,22);
List<Integer> c = Arrays.asList(3,33);
Map<String,List<Integer>> map = new HashMap<>();
map.put("a",a);
map.put("b",b);
map.put("c",c);
Set<String> valid = new HashSet<>();
valid.add("a");
Map<Boolean,List<Map.Entry<String,List<Integer>>>> partitions =
map.entrySet().stream()
.collect(Collectors.partitioningBy(
entry -> valid.contains(entry.getKey())));
System.out.println(partitions);
// partition by the key of the map
// then reduce the values into a single collection
Map<Boolean,List<Integer>> result = map.entrySet().stream()
.collect(Collectors.partitioningBy(
entry -> valid.contains(entry.getKey()),Collectors.mapping(Map.Entry::getValue,Collectors.reducing(new ArrayList<>(),(l1,l2) -> {
l1.addAll(l2);
return l1;
}))));
System.out.println(result);
}
}
我希望最终结果是
{false=[b=[2,22],c=[3,33]],true=[a=[1,11]]}
{false=[2,22,3,33],true=[1,11]}
但是在实际结果中,true和false键都具有全部6个整数:
{false=[b=[2,11]]}
{false=[1,11,2,33]}
请注意,两个分区功能完全相同。但是下游混合了单独分区中的值。这个怎么可能?我认为下游只会在每个分区上运行...
我在这里想念什么?
谢谢。
解决方法
为减少相同的ArrayList引用,在两个分区中均使用。
您可以使用Collectors.toMap
并创建一个合并两个列表的新实例。
Map<Boolean,List<Integer>> result =
map.entrySet()
.stream()
.collect(Collectors.toMap(e -> valid.contains(e.getKey()),Map.Entry::getValue,(l1,l2) -> {
List<Integer> l3 = new ArrayList<>(l1);
l3.addAll(l2);
return l3;
}));
如果您想使用相同的口味
Map<Boolean,List<Integer>> result =
map.entrySet()
.stream()
.collect(Collectors.toMap(e-> valid.contains(e.getKey()),l2) -> Stream.concat(l1.stream(),l2.stream())
.collect(Collectors.toList())));
,
要完成already accepted answer,您可以将Collectors.groupingBy
与Collectors.flatMapping
一起使用分类器,从Java 9开始作为下游收集器。
Map<Boolean,List<Integer>> result = map.entrySet().stream()
.collect(Collectors.groupingBy(
e -> valid.contains(e.getKey()),Collectors.flatMapping(e -> e.getValue().stream(),Collectors.toList())));