分区操作与下游收集器产生了意外结果。

huangapple go评论79阅读模式
英文:

partitioningBy with a downstream collector produced an unexpected result

问题

我有一个测试程序:

    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);
    
            // 根据映射的键进行分区
            // 然后将值减少为单个集合

            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, 22], c=[3, 33]], true=[a=[1, 11]]}
    {false=[1, 11, 2, 22, 3, 33], true=[1, 11, 2, 22, 3, 33]}

注意两个分区函数完全相同。但下游混淆了单独分区中的值。这怎么可能?我认为下游只会对每个分区进行操作…

我在这里漏掉了什么?

谢谢。
英文:

I have a test program:

public class App {

    public static void main(String[] args) {

        List&lt;Integer&gt; a = Arrays.asList(1, 11);
        List&lt;Integer&gt; b = Arrays.asList(2, 22);
        List&lt;Integer&gt; c = Arrays.asList(3, 33);

        Map&lt;String, List&lt;Integer&gt;&gt; map = new HashMap&lt;&gt;();
        map.put(&quot;a&quot;, a);
        map.put(&quot;b&quot;, b);
        map.put(&quot;c&quot;, c);

        Set&lt;String&gt; valid = new HashSet&lt;&gt;();
        valid.add(&quot;a&quot;);

        Map&lt;Boolean, List&lt;Map.Entry&lt;String, List&lt;Integer&gt;&gt;&gt;&gt; partitions =
            map.entrySet().stream()
                .collect(Collectors.partitioningBy(
                    entry -&gt; valid.contains(entry.getKey())));

        System.out.println(partitions);

        // partition by the key of the map
        // then reduce the values into a single collection

        Map&lt;Boolean, List&lt;Integer&gt;&gt; result = map.entrySet().stream()
            .collect(Collectors.partitioningBy(
                entry -&gt; valid.contains(entry.getKey()),
                Collectors.mapping(Map.Entry::getValue,
                                   Collectors.reducing(new ArrayList&lt;&gt;(),
                                                       (l1, l2) -&gt; {
                                                           l1.addAll(l2);
                                                           return l1;
                                                       }))));

        System.out.println(result);
    }
}

I'm expecting the final result to be

{false=[b=[2, 22], c=[3, 33]], true=[a=[1, 11]]}
{false=[2, 22, 3, 33], true=[1, 11]}

But in the actual result, both true and false keys have all 6 integers:

{false=[b=[2, 22], c=[3, 33]], true=[a=[1, 11]]}
{false=[1, 11, 2, 22, 3, 33], true=[1, 11, 2, 22, 3, 33]}

Notice the 2 partitioning functions are exactly the same. But the downstream mixed up the values in the separate partitions. How can that be? I assume the downstream would only operate on each partition...

What did I miss here?

Thanks.

答案1

得分: 1

为了减少相同的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()), Map.Entry::getValue,
                     (l1, l2) -> Stream.concat(l1.stream(), l2.stream())
                                       .collect(Collectors.toList())));
英文:

For reducing same ArrayList reference is used in both partition.
You can use Collectors.toMap and create a new instance merging two lists.

Map&lt;Boolean, List&lt;Integer&gt;&gt; result = 
        map.entrySet()
        .stream()
        .collect(Collectors.toMap(e -&gt; valid.contains(e.getKey()), Map.Entry::getValue, 
        (l1, l2) -&gt; {
          List&lt;Integer&gt; l3 = new ArrayList&lt;&gt;(l1);
          l3.addAll(l2);
          return l3;
        }));

And if you want to use the same taste

Map&lt;Boolean, List&lt;Integer&gt;&gt; result = 
        map.entrySet()
        .stream()
        .collect(Collectors.toMap(e-&gt; valid.contains(e.getKey()), Map.Entry::getValue,
            (l1, l2) -&gt; Stream.concat(l1.stream(), l2.stream())
                              .collect(Collectors.toList())));

答案2

得分: 0

为了完成已经接受的答案,你可以在Java 9中使用Collectors.groupingBy,并结合使用分类器和Collectors.flatMapping作为下游收集器。

Map<Boolean, List<Integer>> result = map.entrySet().stream()
    .collect(Collectors.groupingBy(
         e -> valid.contains(e.getKey()), 
         Collectors.flatMapping(e -> e.getValue().stream(), Collectors.toList())));
英文:

To complete already accepted answer, you can use Collectors.groupingBy using your classifier with Collectors.flatMapping as of Java 9 as a downstream collector.

Map&lt;Boolean, List&lt;Integer&gt;&gt; result = map.entrySet().stream()
    .collect(Collectors.groupingBy(
         e -&gt; valid.contains(e.getKey()), 
         Collectors.flatMapping(e -&gt; e.getValue().stream(), Collectors.toList())));

huangapple
  • 本文由 发表于 2020年8月20日 11:22:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/63497819.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定