在Java中寻找两个列表之间的差异
1.概述
寻找相同数据类型的对象集合之间的差异是一项常见的编程任务。举个例子,设想我们有一个申请考试的学生名单,以及另一个通过考试的学生名单。这两个列表之间的差异将给我们提供没有通过考试的学生。
在Java中,在List API中,没有明确的方法可以找到两个列表之间的差异,尽管有一些辅助方法接近于此。
在这个快速教程中,我们将学习如何找到这两个列表之间的差异。我们将尝试一些不同的方法,包括普通的Java(有和没有Streams),以及第三方库,如Guava和Apache 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 Collections的CollectionUtils 类包含了一个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 Guava和Apache Commons Collections。
我们还讨论了如何处理重复的值。
一如既往,完整的源代码可在GitHub上获得。