1. 概述

在这个教程中,我们将探讨在Java中使用多种方法来检查一个列表中的元素是否也存在于另一个列表中。我们将通过Java流(Streams)、集合(Collection)的disjoint()方法以及Apache Commons库来探索不同的实现方式。

2. 基本等价性检查

这个问题的最简单版本是检查一个列表中的元素是否与另一个列表中的元素相等。这可以是基本值或对象,前提是我们已经设置好对象的比较方式。让我们创建一些列表进行比较:

List<String> listOfLetters = Arrays.asList("a", "b", "c", "d");
List<String> listOfLettersWithOverlap = Arrays.asList("d", "e", "f", "g");
List<String> listOfCities = Arrays.asList("London", "Berlin", "Paris", "Brussels");

字符串“d”出现在前两个列表中,所以我们期望任何解决此问题的方法都能检测到这一点。我们也期望将前两个列表中的任何一个与listOfCities比较会返回负面结果。

2.1. 使用不相交

首先,我们将查看Java集合库中的disjoint()方法。disjoint()方法如果两个指定的集合没有共同的元素,则返回true。因此,由于我们正在寻找两个集合确实有共同元素的情况,我们将使用非运算符反转结果:

@Test
void givenValuesToCompare_whenUsingCollectionsDisjoint_thenDetectElementsInTwoLists() {
    boolean shouldBeTrue = !Collections.disjoint(listOfLetters, listOfLettersWithOverlap);
    assertTrue(shouldBeTrue);

    boolean shouldBeFalse = !Collections.disjoint(listOfLetters, listOfCities);
    assertFalse(shouldBeFalse);
}

如上所示,我们看到两个包含字母的重叠列表返回true,而与城市列表比较后,false值随后返回,这是预期的结果。

2.2. 使用流

Java提供的另一种方法是使用流(Stream)。具体来说,我们将使用anyMatch()方法,该方法如果流中的任何元素匹配给定的谓词则返回true:

@Test
void givenValuesToCompare_whenUsingStreams_thenDetectElementsInTwoLists() {
    boolean shouldBeTrue = listOfLetters.stream()
      .anyMatch(listOfLettersWithOverlap::contains);
    assertTrue(shouldBeTrue);

    boolean shouldBeFalse = listOfLetters.stream()
      .anyMatch(listOfCities::contains);
    assertFalse(shouldBeFalse);
}

anyMatch()方法使用的谓词是调用Collectioncontains()方法。如果集合包含指定的元素,则返回true

2.3. 使用Apache Commons

我们的最后一种方法是使用Apache Commons的CollectionUtils.containsAny()方法。为了使用这个方法,我们需要首先在pom.xml文件中导入依赖:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-collections4</artifactId>
    <version>4.4</version>
 </dependency>

您可以在Maven仓库找到最新版本。准备好后,我们可以像这样使用库:

void givenValuesToCompare_whenUsingApacheCollectionUtils_thenDetectElementsInTwoLists() {
    boolean shouldBeTrue = CollectionUtils.containsAny(listOfLetters, listOfLettersWithOverlap);
    assertTrue(shouldBeTrue);

    boolean shouldBeFalse = CollectionUtils.containsAny(listOfLetters, listOfCities);
    assertFalse(shouldBeFalse);
}

这种方法简洁易读。然而,只有当我们已经在代码中使用了Apache的导入时,才可能使用它,因为Java本身提供了内置的方法。

3. 对象内的属性检查

这个问题的一个更复杂版本是,如果我们想要检查两个列表中的对象是否有匹配的属性。让我们创建一个例子对象来进行这个操作:

class Country {
    String name;
    String language;
    // standard getters, setters and constructors
}

接着,我们可以创建几个Country类的实例,并将它们放入两个列表中:

Country france = new Country("France", "French");
Country mexico = new Country("Mexico", "Spanish");
Country spain = new Country("Spain", "Spanish");
List<Country> franceAndMexico = Arrays.asList(france, mexico);
List<Country> franceAndSpain = Arrays.asList(france, spain);

两个列表中都有一个说法语的国家,所以当比较它们时,我们应该能够发现这一点。

3.1. 使用流

现在,我们使用上述列表,检查是否在两个列表中都存在语言相同的国家。我们可以像在第2.2节中那样使用流。主要的区别在于,我们使用map()方法提取我们感兴趣的属性,例如这里的语言:

@Test
public void givenPropertiesInObjectsToCompare_whenUsingStreams_thenDetectElementsInTwoLists() {
    boolean shouldBeTrue = franceAndMexico.stream()
      .map(Country::getLanguage)
      .anyMatch(franceAndSpain.stream()
        .map(Country::getLanguage)
        .collect(toSet())::contains);

    assertTrue(shouldBeTrue);
}

再次使用anyMatch(),但这次我们将语言收集到一个Set中,并使用contains()检查当前语言是否在Set中。如上所述,我们找到了匹配,因为两个列表中都包含说法语的国家。

4. 总结

在这篇文章中,我们看到流(Streams)是解决这个问题的最灵活方案。我们可以轻松地使用它们来比较整个对象或对象内的属性。此外,我们还探讨了使用Java的disjoint()和Apache的containsAny()方法的简单用例。这两种方法易于使用且产生可读性强的代码。

如往常一样,所有示例的完整代码可在GitHub上找到。