通过 Streams API 比较集合中元素的所有组合

Posted

技术标签:

【中文标题】通过 Streams API 比较集合中元素的所有组合【英文标题】:Comparing all combinations of elements in a Collection via the Streams API 【发布时间】:2021-12-30 09:13:42 【问题描述】:

我正在尝试比较 HashMap 中元素的所有组合(即没有重复比较),并返回满足 HashMap 的所有元素的列表 比较函数,使用 Java 8 Streams-API。 即我想要这样的东西:

// pseudo code, this isnt hashmaps work but hopefully it gets the point across
for(int i=0; i<myHashMap.entrySet().length, i++)
  for(int j=i+1, j<myHashMap.entrySet().length) 
    if(myComparison(entrySet.get(i), entrySet.get(j)))
      myList.add(new myClass[] entrySet.get(i), entrySet.get(j))
  

但使用流 API。我有一个几乎可以使用.forEach 并简单地迭代流两次的版本,但是这存在运行重复比较的问题,从而将这些重复项再次添加到列表中。我相当确定 reduce 操作员能够做到我 需要,但我完全不熟悉 Streams API,无法弄清楚如何达到预期的结果。

【问题讨论】:

如果我正确理解了这个问题,您可以从地图中构建一个集合以删除重复项,然后从集合中构建一个列表。 @Alex 不,重复项是因为与[a, b, c] 一起比较了a, bb, a,这可能会产生相同的结果比较它们。在命令式版本中避免了这种情况,因为ji+1 开始,因此它只遍历其余元素,但是.forEach 总是遍历所有元素。一组不只是修复它,OP 比较所有独特的对。 【参考方案1】:

您需要生成地图的 n 个条目/键的所有不同 k 组合。这篇文章展示了如何实现这样的算法java-combinations-algorithm。但由于我认为任务不是提出这样的算法,我建议使用一个为你完成这项任务的库。这样的库之一是combinatoricslib3。如果你碰巧已经在使用 Apache Commons 或 Guava,它们也有实现组合。

使用 combinatoricslib3 您的代码可能如下所示:

import java.util.Map;
import org.paukov.combinatorics3.Generator;

public class TestJavaClass 
    public static void main(String[] args) 
        Map<String,Integer> myMap = Map.of("Foo", 1, "Bar", 2, "Baz", 3);
        Generator.combination(myMap.keySet())
                .simple(2)
                .stream()
                .forEach(System.out::println);
    

输出:

[Foo, Baz]
[Foo, Bar]
[Baz, Bar]

我使用上面的键组来展示一个简单的例子。但如果您需要这些条目,您可以将其更改为:

Generator.combination(myMap.entrySet())...

获取条目组合:

[Foo=1, Baz=3]
[Foo=1, Bar=2]
[Baz=3, Bar=2]

不确定您的 myComparison 方法是做什么的,但由于您将它用作 if 语句中的条件,我假设它返回一个布尔值。如果是这样,您可以将其用作过滤器以进一步处理流。使用您的方法 myComparison 和自定义类 MyClass 的虚拟实现,如下所示应该为您提供一个起点:

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.paukov.combinatorics3.Generator;
import lombok.AllArgsConstructor;

public class TestJavaClass 

    public static void main(String[] args) 
        Map<String, Integer> myMap = Map.of("Foo", 1, "Bar", 2, "Baz", 3);
        List<MyClass> result =
                Generator.combination(myMap.entrySet())
                        .simple(2)
                        .stream()
                        .filter(comb -> myComparison(comb.get(0), comb.get(1)))
                        .map(comb -> new MyClass(comb.get(0), comb.get(1)))
                        .collect(Collectors.toList());
    

    static boolean myComparison(Map.Entry<String, Integer> first, Map.Entry<String, Integer> second) 
        return first.getValue() + second.getValue() <= 4;
    

    @AllArgsConstructor
    static class MyClass 

        Map.Entry<String, Integer> one;
        Map.Entry<String, Integer> two;
    

【讨论】:

我在这个网站上收到的最佳答案,非常感谢您提供的丰富信息! 不客气。我很高兴能帮上忙。

以上是关于通过 Streams API 比较集合中元素的所有组合的主要内容,如果未能解决你的问题,请参考以下文章

java Streams API介绍

Java 8 中的 Streams API 详解

JDK8-Java Streams 收集器

Java 8 中的 Streams API

Java 8 中的 Streams API 详解

Java 8 中的 Streams API 详解