Java 创建所有可能的元素组合

Java 创建所有可能的元素组合,java,algorithm,performance,java-stream,Java,Algorithm,Performance,Java Stream,我需要创建一些键的所有可能组合,这些键由X(在我的例子中是8)组成,这些元素同样重要。所以我想出了这样的代码: final LinkedList<Key> keys = new LinkedList(); firstElementCreator.getApplicableElements() // All creators return a Set of elements .forEach( first -> secondElementCreator.ge

我需要创建一些键的所有可能组合,这些键由X(在我的例子中是8)组成,这些元素同样重要。所以我想出了这样的代码:

final LinkedList<Key> keys = new LinkedList();

firstElementCreator.getApplicableElements() // All creators return a Set of elements
          .forEach( first -> secondElementCreator.getApplicableElements()
           .forEach( second -> thirdElementCreator.getApplicableElements()
            // ... more creators
           .forEach( X -> keys.add( new Key( first, second, third, ..., X ) ) ) ) ) ) ) ) );

return keys;
final LinkedList key=new LinkedList();
firstElementCreator.GetApplicatableElements()//所有创建者都返回一组元素
.forEach(first->secondElementCreator.getApplicatableElements()
.forEach(第二->第三个delementCreator.getApplicatableElements()
//…更多的创造者
.forEach(X->keys.add(新键(第一、第二、第三、…、X)));
返回键;
它正在工作,但是有X嵌套的forEach,我觉得我错过了一个更简单/更好/更优雅的解决方案。有什么建议吗?
提前谢谢

是笛卡尔积吗?许多库提供API,例如:和Guava:

List<ApplicableElements> elementsList = Lists.newArrayList(firstElementCreator, secondElementCreator...).stream()
        .map(c -> c.getApplicableElements()).collect(toList());

List<Key> keys = Lists.cartesianProduct(elementsList).stream()
        .map(l -> new Key(l.get(0), l.get(1), l.get(2), l.get(3), l.get(4), l.get(5), l.get(6), l.get(7))).collect(toList());
List elementsList=Lists.newArrayList(firstElementCreator,secondElementCreator…).stream()
.map(c->c.getApplicatableElements()).collect(toList());
列表键=Lists.cartesianProduct(elementsList.stream())
.map(l->newkey(l.get(0)、l.get(1)、l.get(2)、l.get(3)、l.get(4)、l.get(5)、l.get(6)、l.get(7)).collect(toList());

是笛卡尔积吗?许多库提供API,例如:和Guava:

List<ApplicableElements> elementsList = Lists.newArrayList(firstElementCreator, secondElementCreator...).stream()
        .map(c -> c.getApplicableElements()).collect(toList());

List<Key> keys = Lists.cartesianProduct(elementsList).stream()
        .map(l -> new Key(l.get(0), l.get(1), l.get(2), l.get(3), l.get(4), l.get(5), l.get(6), l.get(7))).collect(toList());
List elementsList=Lists.newArrayList(firstElementCreator,secondElementCreator…).stream()
.map(c->c.getApplicatableElements()).collect(toList());
列表键=Lists.cartesianProduct(elementsList.stream())
.map(l->newkey(l.get(0)、l.get(1)、l.get(2)、l.get(3)、l.get(4)、l.get(5)、l.get(6)、l.get(7)).collect(toList());

由于输入集的数量是固定的(它必须与密钥构造函数中的参数数量相匹配),因此您的解决方案实际上是不错的

但是,如果没有lambdas,它会更高效、更容易阅读,比如:

for (Element first : firstElementCreator.getApplicableElements()) {
    for (Element second : secondElementCreator.getApplicableElements()) {
        for (Element third : thirdElementCreator.getApplicableElements()) {
            keys.add(new Key(first, second, third));
        }
    }
}

由于输入集的数量是固定的(它必须与键构造函数中的参数数量相匹配),因此您的解决方案实际上是不错的

但是,如果没有lambdas,它会更高效、更容易阅读,比如:

for (Element first : firstElementCreator.getApplicableElements()) {
    for (Element second : secondElementCreator.getApplicableElements()) {
        for (Element third : thirdElementCreator.getApplicableElements()) {
            keys.add(new Key(first, second, third));
        }
    }
}

规范的解决方案是使用
flatMap
。然而,棘手的部分是从多个输入级别创建
对象

直接的方法是在最内部的函数中进行计算,其中每个值都在范围内

final List<Key> keys = firstElementCreator.getApplicableElements().stream()
  .flatMap(first -> secondElementCreator.getApplicableElements().stream()
    .flatMap(second -> thirdElementCreator.getApplicableElements().stream()
      // ... more creators
      .map( X -> new Key( first, second, third, ..., X ) ) ) )
  .collect(Collectors.toList());
(假设
String
作为元素类型),我们可以使用

final List<Key> keys =
    firstElementCreator.getApplicableElements().stream().map(Key::new)
      .flatMap(e -> secondElementCreator.getApplicableElements().stream().map(e::add))
      .flatMap(e -> thirdElementCreator.getApplicableElements().stream().map(e::add))
      // ... more creators
      .collect(Collectors.toList());

在这里,每个创建者都映射到上一个解决方案的相同流生成函数,然后将所有函数简化为单个函数,将每个函数与
flatMap
步骤组合到下一个步骤,最后执行生成的函数以获得流,然后将其收集到
列表中

标准解决方案是使用
flatMap
。然而,棘手的部分是从多个输入级别创建
对象

直接的方法是在最内部的函数中进行计算,其中每个值都在范围内

final List<Key> keys = firstElementCreator.getApplicableElements().stream()
  .flatMap(first -> secondElementCreator.getApplicableElements().stream()
    .flatMap(second -> thirdElementCreator.getApplicableElements().stream()
      // ... more creators
      .map( X -> new Key( first, second, third, ..., X ) ) ) )
  .collect(Collectors.toList());
(假设
String
作为元素类型),我们可以使用

final List<Key> keys =
    firstElementCreator.getApplicableElements().stream().map(Key::new)
      .flatMap(e -> secondElementCreator.getApplicableElements().stream().map(e::add))
      .flatMap(e -> thirdElementCreator.getApplicableElements().stream().map(e::add))
      // ... more creators
      .collect(Collectors.toList());

在这里,每个创建者都映射到上一个解决方案的相同流生成函数,然后将所有函数简化为单个函数,将每个函数与
flatMap
步骤组合到下一个步骤,最后执行生成的函数以获得流,当你需要任意数量的循环时,递归几乎总是答案。当你需要任意数量的循环时,递归几乎总是答案。这是我一直在寻找的答案。尽管如此,这个解决方案还是有一个缺点:我的每个元素都有不同的类型(现在我知道我没有提到),所以类型转换是必要的。也许可以创建一些公共接口,我会考虑一下。谢谢。这是我一直在寻找的答案。尽管如此,这个解决方案还是有一个缺点:我的每个元素都有不同的类型(现在我知道我没有提到),所以类型转换是必要的。也许可以创建一些公共接口,我会考虑一下。谢谢。你是对的,流并不总是最好的解决方案,这看起来肯定更好,谢谢。你是对的,流并不总是最好的解决方案,这看起来肯定更好,谢谢。