Намиране на разликите между два списъка в Java

1. Общ преглед

Намирането на разлики между колекции от обекти от същия тип данни е често срещана задача за програмиране. Като пример, представете си, че имаме списък със студенти, кандидатствали за изпит, и друг списък със студенти, които са го издържали. Разликата между тези два списъка ще ни даде студентите, които не са издържали изпита.

В Java няма изричен начин за намиране на разлики между два списъка в API на списъка , въпреки че има някои помощни методи, които се доближават.

В този бърз урок ще разгледаме как да намерим разликите между двата списъка . Ще изпробваме няколко различни подхода, включително обикновена Java (със и без потоци ) и използване на библиотеки на трети страни като 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. Използване на API на Java List

Ние можем да създадем копие на един списък и след това извадете всички общи елементи на с другата , като се използва Списък метод removeAll () :

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");

Трябва също да отбележим, че ако искаме да намерим общите елементи между двата списъка, Списъкът съдържа и метод retainAll .

4. Използване на API за потоци

Java Stream може да се използва за извършване на последователни операции върху данни от колекции, което включва филтриране на разликите между списъците :

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

Guava съдържа удобни комплекти . метод за разлика , но за да го използваме, трябва първо да преобразуваме нашия Списък в набор :

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

Трябва да отбележим, че преобразуването на списъка в набор ще доведе до дедупликацията и пренареждането му.

5.2. Използване на колекции Apache Commons

Класът CollectionUtils от Apache Commons Collections съдържа метод removeAll .

Този метод прави същото като Списък . removeAll , като същевременно създавате нова колекция за резултата :

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");

Също така можем да постигнем това, като използваме метода на изваждане от колекциите на 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.