使用 Streams 模拟嵌套 for 循环过滤

Simulating nested for-loop filtering using Streams

我正在做一个与口袋妖怪有关的个人项目。这个程序与口袋妖怪有关的事实并不重要,但我认为用口袋妖怪相关的术语更容易理解,所以我会这样解释,任何对口袋妖怪不太了解的人都会回答如果他们愿意,可以随意使用更通用的术语:

我有一个 PokemonRecord 记录,它有 2 个参数用于 2 PokemonTypesPokemonRecord 的任何实例都可以有 1 种或 2 种类型。如果 PokemonRecord 只有一种类型,则 type2 == null。以下方法的目的是采用 PokemonTypes 的数组并生成所有可能的 PokemonRecord 类型组合的列表,这些组合将抵抗所有这些给定类型。如果您不知道“抵抗类型”是什么意思,那么您可以将其视为测试条件和 returns a boolean.

的单独方法

这是预期输出的示例:

Enter the name of a Pokemon type: water
Enter the name of a Pokemon type: ground
Enter the name of a Pokemon type: grass
Enter the name of a Pokemon type: done

The following type combinations resist all of [water, ground, grass]:
Grass
Normal & Grass
Grass & Ice
Grass & Fighting
Grass & Flying
Grass & Psychic
Grass & Bug
Grass & Ghost
Grass & Dragon
Grass & Dark
Grass & Fairy
Flying & Dragon
Bug & Dragon

目前,我的代码按预期工作;然而,回过头来看,我想写一些不同的东西——在国际象棋中,当你找到一个好的着法时,找到一个更好的着法。我最初使用程序 for 循环方法来过滤 PokemonTypes 的完整列表并测试它们的每个组合:

public static List<PokemonRecord> genMonResToAll(PokemonTypes... types) {
    List<PokemonTypes> allTypes = //List of possible PokemonTypes that this Pokemon can have (PokemonTypes that are not weak to any inputted PokemonTypes)
    List<PokemonRecord> outputList = new ArrayList<>();
    
    //Add any single-type Pokemon that resists all types
    for(PokemonTypes type : allTypes)
            if(new PokemonRecord(type).isResistantToAll(types))
                outputList.add(new PokemonRecord(type));

    //Add any multi-type Pokemon that resists all types
    for (int i = 0; i < allTypes.size() - 1; i++)
            for (int j = i + 1; j < allTypes.size(); j++) {
                PokemonRecord testMon = new PokemonRecord(allTypes.get(i), allTypes.get(j));
                if (testMon.isResistantToAll(types))
                    otuputList.add(testMon);
            }
    return outputList;
}

//The functionality of any specific `Pokemon` or `PokemonTypes` method used isn't relevant, they all work as intended.

我现在正在尝试使用 Stream API 重写此代码以使其更具声明性。我能够弄清楚如何将第一个循环(添加单一类型 PokemonRecord 的循环)转换为基于流的声明语句。我很难理解第二个问题。我当前重构第一个循环的代码是:

public static List<PokemonRecord> genMonResToAll(PokemonTypes... types) {
    List<PokemonTypes> allTypes = //List of possible PokemonTypes that this Pokemon can have (PokemonTypes that are not weak to any inputted PokemonTypes)
    
    //Add any single-type Pokemon that resists all types
    List<PokemonRecord> outputList= allTypes.stream()
    .map(PokemonRecord::new)
    .filter(x -> x.isResistantToAll(types))
    .collect(Collectors.toList());

    //Add any multi-type Pokemon that resists all types
    for (int i = 0; i < allTypes.size() - 1; i++)
            for (int j = i + 1; j < allTypes.size(); j++) {
                PokemonRecord testMon = new PokemonRecord(allTypes.get(i), allTypes.get(j));
                if (testMon.isResistantToAll(types))
                    otuputList.add(testMon);
            }
    return outputList;
}

//The functionality of any specific `Pokemon` or `PokemonTypes` method used isn't relevant, they all work as intended.

感谢任何帮助,如果您需要任何说明,请随时询问!

由于 PokemonRecord 是二维的,我认为您不应该使用流。这是没有流的更好方法:

  1. 如果您还没有这样做,这是 PokemonRecord 的干净实现,它保证 PokemonRecord(type1, type2) == PokemonRecord(type2, type1)。以这种方式防止任何非法 non-equal 对象通常是个好主意:
public record PokemonRecord(PokemonType type1, PokemonType type2) {
    
    public PokemonRecord(PokemonType type1, PokemonType type2) {
        if (type1 == type2) throw new IllegalArgumentException("Illegal type combination");
        boolean order = type1 != null && (type2 == null || type1.compareTo(type2) < 0);
        this.type1 = order ? type1 : type2;
        this.type2 = order ? type2 : type1;
    }

    // your methods
}
  1. 现在只需将 null 添加到 allTypes 并对其进行楼梯迭代(以获得所有可能的组合而不会重复):
public static List<PokemonRecord> genMonResToAll(PokemonType... types) {
    List<PokemonType> allTypes = new ArrayList<>();
    allTypes.add(null);
    List<PokemonRecord> result = new ArrayList<>();
    for (int s = allTypes.size(), i = 0; i < s; i++) for (int j = s - 1; j > i; j--) {
        PokemonRecord record = new PokemonRecord(allTypes.get(i), allTypes.get(j));
        if (record.isResistantToAll(types)) result.add(record);
    }
    return result;
}