Рубрики
Без рубрики

Поиск различий между двумя списками в Java

Мы исследуем, как найти элементы, присутствующие в одном списке, но не в другом, включая списки с несколькими копиями одного и того же элемента.

Автор оригинала: baeldung.

1. Обзор

Поиск различий между коллекциями объектов одного и того же типа данных является общей задачей программирования. В качестве примера представьте, что у нас есть список студентов, которые подали заявку на экзамен, и еще один список студентов, которые его сдали. Разница между этими двумя списками даст нам студентов, которые не сдали экзамен.

В Java нет явного способа найти различия между двумя списками в List API , хотя есть некоторые вспомогательные методы, которые подходят близко.

В этом кратком руководстве мы рассмотрим, как найти различия между двумя списками . Мы попробуем несколько различных подходов, включая простой Java (с и без потоков ) и использование сторонних библиотек, таких как Guava и коллекции Apache Commons .

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. Использование API списка Java

Мы можем создать копию одного списка , а затем удалить все элементы, общие с другим , используя метод List | remove All() :

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

Давайте перевернем это, чтобы найти различия наоборот:

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

Следует также отметить, что если мы хотим найти общие элементы между двумя списками, List также содержит метод retainAll .

4. Использование API потоков

Java Поток может использоваться для выполнения последовательных операций с данными из коллекций, включая фильтрацию различий между списками :

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

Как и в нашем первом примере, мы можем переключить порядок списков, чтобы найти различные элементы из второго списка:

List 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

Гуава содержит удобный Наборы . разница метод , но для его использования нам нужно сначала преобразовать наш Список в Набор :

List 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

Класс CollectionUtils из коллекций Apache Commons содержит метод removeAll .

Этот метод выполняет то же самое, что и List . удалить все , а также создать новую коллекцию для результата :

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

6. Обработка Повторяющихся Значений

Давайте теперь рассмотрим поиск различий, когда два списка содержат дублированные значения.

Для этого нам нужно удалить повторяющиеся элементы из первого списка ровно столько раз, сколько они содержатся во втором списке.

В нашем примере значение “Jack” появляется дважды в первом списке и только один раз во втором списке:

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

Мы также можем достичь этого с помощью метода subtract из коллекций Apache Commons :

List 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 .