Trovare le differenze tra due elenchi in Java

1. Panoramica

Trovare le differenze tra le raccolte di oggetti dello stesso tipo di dati è un'attività di programmazione comune. Ad esempio, immagina di avere un elenco di studenti che hanno presentato domanda per un esame e un altro elenco di studenti che lo hanno superato. La differenza tra queste due liste ci darebbe gli studenti che non hanno superato l'esame.

In Java , non esiste un modo esplicito per trovare differenze tra due elenchi nell'API List , sebbene esistano alcuni metodi di supporto che si avvicinano.

In questo breve tutorial, vedremo come trovare le differenze tra i due elenchi . Proveremo alcuni approcci diversi, incluso Java semplice (con e senza stream ) e l'utilizzo di librerie di terze parti come Guava e Apache Commons Collections .

2. Configurazione di prova

Iniziamo definendo due elenchi, che useremo per testare i nostri esempi:

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. Utilizzo dell'API dell'elenco Java

Possiamo creare una copia di una lista e poi rimuovere tutti gli elementi comuni con l'altra , utilizzando il metodo List removeAll () :

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

Invertiamo questo per trovare le differenze al contrario:

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

Dobbiamo anche notare che se vogliamo trovare gli elementi comuni tra i due elenchi, List contiene anche un metodo keepAll .

4. Utilizzo dell'API Streams

È possibile utilizzare un flusso Java per eseguire operazioni sequenziali sui dati delle raccolte, che include il filtraggio delle differenze tra gli elenchi :

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

Come nel nostro primo esempio, possiamo cambiare l'ordine degli elenchi per trovare i diversi elementi dal secondo elenco:

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

Dobbiamo notare che la ripetuta chiamata di List . contiene () può essere un'operazione costosa per elenchi più grandi.

5. Utilizzo di librerie di terze parti

5.1. Utilizzando Google Guava

Guava contiene un pratico set . metodo di differenza , ma per usarlo dobbiamo prima convertire il nostro List in un Set :

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

Dobbiamo notare che la conversione dell'elenco in un set avrà l'effetto di deduplicarlo e riordinarlo.

5.2. Utilizzo delle collezioni Apache Commons

La classe CollectionUtils di Apache Commons Collections contiene un metodo removeAll .

Questo metodo funziona come List . removeAll , creando anche una nuova raccolta per il risultato :

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

6. Gestione dei valori duplicati

Vediamo ora come trovare differenze quando due elenchi contengono valori duplicati.

Per ottenere ciò, dobbiamo rimuovere gli elementi duplicati dal primo elenco, esattamente tante volte quante sono contenuti nel secondo elenco.

Nel nostro esempio, il valore "Jack" appare due volte nel primo elenco e solo una volta nel secondo elenco:

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

Possiamo anche ottenere questo risultato utilizzando il metodo di sottrazione dalle raccolte di Apache Commons :

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

7. Conclusione

In questo articolo, abbiamo esplorato alcuni modi per trovare differenze tra gli elenchi .

Negli esempi, abbiamo coperto una soluzione Java di base , una soluzione che utilizza l' API Streams e librerie di terze parti come Google Guava e Apache Commons Collections.

Abbiamo anche visto come gestire i valori duplicati.

Come sempre, il codice sorgente completo è disponibile su GitHub.