在Java中寻找两个列表之间的差异

评论 0 浏览 0 2020-08-12

1.概述

寻找相同数据类型的对象集合之间的差异是一项常见的编程任务。举个例子,设想我们有一个申请考试的学生名单,以及另一个通过考试的学生名单。这两个列表之间的差异将给我们提供没有通过考试的学生。

Java中,在List API中,没有明确的方法可以找到两个列表之间的差异,尽管有一些辅助方法接近于此。

在这个快速教程中,我们将学习如何找到这两个列表之间的差异。我们将尝试一些不同的方法,包括普通的Java(有和没有Streams),以及第三方库,如GuavaApache Commons Collections

2.测试设置

让我们从定义两个列表开始,我们将用这两个列表来测试我们的例子。

public class FindDifferencesBetweenListsUnitTest {

    private static final List listOne = Arrays.asList("Jack", "Tom", "Sam", "John", "James", "Jack");
    private static final List listTwo = Arrays.asList("Jack", "Daniel", "Sam", "Alan", "James", "George");

}

3.使用Java List API

我们可以创建一个列表的副本,然后使用List方法removeAll(),删除所有与其他共同的元素。

List<String> differences = new ArrayList<>(listOne);
differences.removeAll(listTwo);
assertEquals(2, differences.size());
assertThat(differences).containsExactly("Tom", "John");

让我们反过来找找另一个方向的差异。

List<String> differences = new ArrayList<>(listTwo);
differences.removeAll(listOne);
assertEquals(3, differences.size());
assertThat(differences).containsExactly("Daniel", "Alan", "George");

我们还应该注意到,如果我们想找到两个列表之间的共同元素,List也包含一个retainAll 方法。

4.使用Streams API

Java Stream 可用于对来自集合的数据进行顺序操作,这包括过滤列表之间的差异

List<String> differences = listOne.stream()
            .filter(element -> !listTwo.contains(element))
            .collect(Collectors.toList());
assertEquals(2, differences.size());
assertThat(differences).containsExactly("Tom", "John");

就像我们的第一个例子一样,我们可以切换列表的顺序,从第二个列表中找到不同的元素。

List<String> differences = listTwo.stream()
            .filter(element -> !listOne.contains(element))
            .collect(Collectors.toList());
assertEquals(3, differences.size());
assertThat(differences).containsExactly("Daniel", "Alan", "George");

我们应该注意到,重复调用List.contains()对于较大的列表来说,可能是一个昂贵的操作。

5.使用第三方库

5.1.使用Google Guava

Guava包含了一个方便的Sets.difference方法,但要使用它,我们需要先将我们的List转换为Set

List<String> differences = new ArrayList<>(Sets.difference(Sets.newHashSet(listOne), Sets.newHashSet(listTwo)));
assertEquals(2, differences.size());
assertThat(differences).containsExactlyInAnyOrder("Tom", "John");

我们应该注意到,将List转换为Set将产生复制和重新排序的效果。

5.2.使用Apache Commons Collections

来自Apache Commons CollectionsCollectionUtils 类包含了一个removeAll 方法。

这个方法与List.removeAll的做法相同,同时还为结果创建了一个新的集合。

List<String> differences = new ArrayList<>((CollectionUtils.removeAll(listOne, listTwo)));
assertEquals(2, differences.size());
assertThat(differences).containsExactly("Tom", "John");

6.处理重复值

现在让我们来看看当两个列表包含重复的值时,如何找出其中的差异。

为实现这一点,我们需要从第一个列表中删除重复元素,精确地删除它们在第二个列表中包含的次数。

在我们的例子中,数值“Jack”在第一个列表中出现了两次,而在第二个列表中只出现了一次。

List<String> differences = new ArrayList<>(listOne);
listTwo.forEach(differences::remove);
assertThat(differences).containsExactly("Tom", "John", "Jack");

我们也可以使用Apache Commons Collections中的subtract方法来实现这一点。

List<String> differences = new ArrayList<>(CollectionUtils.subtract(listOne, listTwo));
assertEquals(3, differences.size());
assertThat(differences).containsExactly("Tom", "John", "Jack");

7.结语

在这篇文章中,我们探讨了一些寻找列表之间差异的方法。我们涵盖了一个基本的Java解决方案使用Streams API的解决方案,以及使用第三方库的解决方案,如Google GuavaApache Commons Collections

我们还讨论了如何处理重复的值。

一如既往,完整的源代码可在GitHub上获得。

最后更新2022-12-28
0 个评论
标签