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

Comparing all combinations of elements in a Collection via the Streams API

我正在尝试比较 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,无法弄清楚如何实现所需的结果。

您需要为地图的 n entries/keys 生成所有不同的 k 组合。 post 展示了如何实现这样的算法 java-combinations-algorithm. But since I think the task is not to come up with such an algorithm, I would suggest to use a librarry which does that task for you. One of such library is 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 语句中的条件,我假设它 returns 是一个布尔值。如果是这样,您可以将其用作过滤器以进一步处理流。通过你的方法 myComparison 和你的自定义 class 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;
    }
}