【问题标题】:Finding matches in a list to create a new list using Java 8在列表中查找匹配项以使用 Java 8 创建新列表
【发布时间】:2019-12-17 11:14:35
【问题描述】:

我有一个包含对象的列表,即狗。狗可以是雄性雌性。这是 Object 中的一个变量。

我需要遍历 Dogs,对于每只公狗,我需要查看是否有与公狗的两个变量匹配的母狗。

Dog.getName()Dog.getAge()。所以我会有一个男性和女性匹配他们的名字和年龄。

如果我找到匹配项,我需要将两条 Dogs 添加到新列表中。

弄清楚如何使用 Java 8 来做这件事让我发疯了,并且真的可以使用一些建议。

我已经完成了一些代码,但想知道我是否可以让它更有效率。

到目前为止我要做的事情:

List<Dog> dogsA = unmatchedDogs
        .stream()
        .filter(
                dogA ->{
                    return dogA.getGender().equalsIgnoreCase("Male");
                }
        )
        .collect(Collectors.toList());

List<Dog> dogsB = unmatchedDogs
        .stream()
        .filter(
                dogB ->{
                    return dogB.getGender().equalsIgnoreCase("Female");
                }
        )
        .collect(Collectors.toList());

List<Dog> dogsToUpdate = new ArrayList<>();

    dogsA.stream()
            .flatMap(x -> dogsB
            .stream()
            .filter(y -> x.getAge().equalsIgnoreCase(y.getAge()))
            .filter(y -> x.getName().equalsIgnoreCase(y.getName()))
            .limit(1))
            .forEach(product -> dogsToUpdate.add(product));

            dogsB.stream()
            .flatMap(x -> dogsA
            .stream()
            .filter(y -> x.getAge().equalsIgnoreCase(y.getAge()))
            .filter(y -> x.getName().equalsIgnoreCase(y.getName()))
            .limit(1))
            .forEach(product -> dogsToUpdate.add(product));

【问题讨论】:

  • 你的代码有什么问题?
  • “如何使用 Java 8 循环”——什么是 Java 8 循环?在您的代码中,我只能看到一些流,而不是单个循环。
  • 对不起,我编辑了标题,我正在努力让它不那么麻烦。伸出手看看是否有更有效的方法
  • @Random 如果某些答案对您有所帮助并确实解决了您的问题,请接受它,因此问题将被关闭

标签: java arrays loops nested


【解决方案1】:

具有讽刺意味的是,使用循环方法它会变得可读且更短,而且它仍然是 O(n^2)。 :

for (int i = 0; i < dogsA.size(); i++) {
    for (int j = 0; j < dogsB.size(); j++) {
        Dog male = dogsA.get(i);
        Dog female = dogsB.get(j);

        if (male.getAge() == female.getAge() 
            && male.getName().equals(female.getName())) {
            dogsToUpdate.add(male);
            dogsToUpdate.add(female);
        }
    }
}

附: Stream API 应该尽可能的使用,但是,如果它使逻辑看起来更复杂并且没有提供额外的效率,你应该看看简单的方法。如果您不想使用循环,请告诉我,我将编辑我的答案并尝试提出流 API 解决方案。

【讨论】:

  • 谢谢。我有一个类似的解决方案,但决定尝试合并流 API 以获得一些经验,因为我没有太多机会使用 Java 8
  • 那么你应该看看 Joop Eggen 的解决方案,但是,我建议使用 Java 8 的特性,它们在简单和方便方面会真正让你受益
  • @Random 您在第二个 for 循环中缺少右括号。同样对于您可能想要使用的名称equals(),而不是==
  • @Bentaye 你是对的,谢谢 - 我编辑了我的答案
  • @Random 恐怕你还是漏掉了if 末尾的括号:)
【解决方案2】:

以下所有示例都假设您首先将您的狗按雄性和雌性分类,然后调用match 方法:

List<Dog> females = dogs.stream()
        .filter(d -> d.getGender().equalsIgnoreCase("Female"))
        .collect(Collectors.toList());

List<Dog> males = dogs.stream()
        .filter(d -> d.getGender().equalsIgnoreCase("Male"))
        .collect(Collectors.toList());

return match(males, females);

版本 1:Streams

我将首先创建一种方法来在给定男性的女性列表中找到匹配的女性。此方法返回一个Optional&lt;List&lt;Dog&gt;&gt;。如果匹配则包含男性和女性,如果不匹配则返回Optional.empty

public Optional<List<Dog>> findMatchingFemale(Dog male, List<Dog> females) {
    return females.stream()
            .filter(female -> female.getName().equalsIgnoreCase(male.getName()) &&
                              female.getAge() == male.getAge())
            .findFirst()
            .map(female -> Arrays.asList(male, female));
}

然后我会创建女性列表,然后遍历男性列表并使用上述方法在女性列表中为每个人寻找匹配的女性。然后过滤掉 Optional.emptyflatMap 整个事情:

public List<Dog> match(List<Dog> males, List<Dog> females) {
    return males.stream()
            .map(male -> findMatchingFemale(male, females)) // find matching couples
            .filter(Optional::isPresent)                    // filter out single males
            .map(Optional::get)                             // get the couples a List of lists
            .flatMap(List::stream)                          // flatten to a list
            .collect(Collectors.toList());
}

版本 2:foreach

你也可以用循环来做,但不是for,而是使用foreach,这样你就可以摆脱索引,它有一个流填充(但它不是)

public List<Dog> match(List<Dog> males, List<Dog> females) {
    List<Dog> dogsToUpdate = new ArrayList<>();
    males.forEach(male ->
            females.forEach(female ->
                    addIfMatch(male, female, dogsToUpdate)));
    return dogsToUpdate;
}

private void addIfMatch(Dog male, Dog female, List<Dog> dogs) {
    if (male.getAge() == female.getAge() &&
        male.getName().equalsIgnoreCase(female.getName())) {
        dogs.add(male);
        dogs.add(female);
    }
}

第 3 版另一个带有 Streams 的版本,因为您需要 Streams 的示例

public List<Dog> match(List<Dog> males, List<Dog> females) {
    return males.stream()
            .map(male -> females.stream()
                    .map(female -> keepIfMatch(male, female))
                    .filter(Optional::isPresent)
                    .map(Optional::get)
                    .flatMap(List::stream)
                    .collect(Collectors.toList()))
            .flatMap(List::stream)
            .collect(Collectors.toList());
}

private Optional<List<Dog>> keepIfMatch(Dog male, Dog female) {
    return male.getAge() == female.getAge() && male.getName().equalsIgnoreCase(female.getName()) ?
            Optional.of(Arrays.asList(male, female)) :
            Optional.empty();
}

【讨论】:

    【解决方案3】:

    以下应该做的

    List<Dog> maleDogs = unmatchedDogs
            .stream()
            .filter(dog -> dog.getGender().equalsIgnoreCase("Male"));
            .collect(Collectors.toList());
    
    List<Dog> femaleDogs = unmatchedDogs
            .stream()
            .filter(dog -> return dog.getGender().equalsIgnoreCase("Female"));
            .collect(Collectors.toList());
    
    List<Dog[]> dogPairs = new ArrayList<>();
    maleDogs.forEach(male -> {
        femaleDogs.stream()
                  .filter(female -> dogsMatch(male, female))
                  .findAny()
                  .ifPresent(female -> {
                      femaleDogs.remove(female);
                      dogPairs.add(new Dog[] { male, female };
    
                      unmatchedDogs.remove(male));
                      unmatchedDogs.remove(female));
                  });
    
    boolean dogsMatch(Dog male, Dog female) {
        return male.getAge() == female.getAge()
            && male.getKind().equalsIgnoreCase(female.getKind());
    }
    

    它不会让一个女性匹配几个男性。

    【讨论】:

      猜你喜欢
      • 2019-09-21
      • 1970-01-01
      • 1970-01-01
      • 2017-09-12
      • 1970-01-01
      • 1970-01-01
      • 2015-10-23
      • 2021-01-07
      • 2020-09-11
      相关资源
      最近更新 更多