Come contare gli elementi duplicati in Arraylist

1. Panoramica

In questo breve tutorial, esamineremo alcuni modi diversi per contare gli elementi duplicati in un ArrayList .

2. Loop con Map.put ()

Il nostro risultato atteso sarebbe un oggetto Map , che contiene tutti gli elementi dall'elenco di input come chiavi e il conteggio di ogni elemento come valore.

La soluzione più semplice per ottenere ciò sarebbe scorrere l'elenco di input e per ogni elemento:

  • se la resultMap contiene l'elemento, incrementiamo un contatore di 1
  • in caso contrario, abbiamo messo una nuova voce di mappa (elemento, 1) alla mappa
public  Map countByClassicalLoop(List inputList) { Map resultMap = new HashMap(); for (T element : inputList) { if (resultMap.containsKey(element)) { resultMap.put(element, resultMap.get(element) + 1L); } else { resultMap.put(element, 1L); } } return resultMap; }

Questa implementazione ha la migliore compatibilità, poiché funziona per tutte le moderne versioni di Java.

Se non abbiamo bisogno della compatibilità pre-Java 8, possiamo semplificare ulteriormente il nostro metodo:

public  Map countByForEachLoopWithGetOrDefault(List inputList) { Map resultMap = new HashMap(); inputList.forEach(e -> resultMap.put(e, resultMap.getOrDefault(e, 0L) + 1L)); return resultMap; }

Successivamente, creiamo un elenco di input per testare il metodo:

private List INPUT_LIST = Lists.list( "expect1", "expect2", "expect2", "expect3", "expect3", "expect3", "expect4", "expect4", "expect4", "expect4"); 

E ora verificiamolo:

private void verifyResult(Map resultMap) { assertThat(resultMap) .isNotEmpty().hasSize(4) .containsExactly( entry("expect1", 1L), entry("expect2", 2L), entry("expect3", 3L), entry("expect4", 4L)); } 

Riutilizzeremo questo cablaggio di prova per il resto dei nostri approcci.

3. Loop con Map.compute ()

In Java 8, il pratico metodo compute () è stato introdotto nell'interfaccia Map . Possiamo utilizzare anche questo metodo:

public  Map countByForEachLoopWithMapCompute(List inputList) { Map resultMap = new HashMap(); inputList.forEach(e -> resultMap.compute(e, (k, v) -> v == null ? 1L : v + 1L)); return resultMap; }

Avviso (k, v) -> v == null? 1L: v + 1L è la funzione di rimappatura che implementa l' interfaccia BiFunction . Per una data chiave, restituisce il valore corrente incrementato di uno (se la chiave è già presente nella mappa) o restituisce il valore predefinito di uno.

Per rendere il codice più leggibile, potremmo estrarre la funzione di rimappatura nella sua variabile o addirittura prenderla come parametro di input per countByForEachLoopWithMapCompute.

4. Loop con Map.merge ()

Quando si utilizza Map.compute () , è necessario gestire i valori null in modo esplicito, ad esempio se non esiste una mappatura per una determinata chiave. Questo è il motivo per cui abbiamo implementato un controllo nullo nella nostra funzione di rimappatura. Questo, tuttavia, non sembra carino.

Puliamo ulteriormente il nostro codice con l'aiuto del metodo Map.merge () :

public  Map countByForEachLoopWithMapMerge(List inputList) { Map resultMap = new HashMap(); inputList.forEach(e -> resultMap.merge(e, 1L, Long::sum)); return resultMap; }

Ora il codice sembra pulito e conciso.

Spieghiamo come funziona merge () . Se la mappatura per una determinata chiave non esiste o il suo valore è nullo , associa la chiave al valore fornito. In caso contrario, calcola un nuovo valore utilizzando la funzione di rimappatura e aggiorna la mappatura di conseguenza.

Si noti che questa volta abbiamo utilizzato Long :: sum come implementazione dell'interfaccia BiFunction .

5. Stream API Collectors.toMap ()

Dato che abbiamo già parlato di Java 8, non possiamo dimenticare la potente Stream API. Grazie all'API Stream, possiamo risolvere il problema in modo molto compatto.

Il raccoglitore toMap () ci aiuta a convertire l'elenco di input in una mappa :

public  Map countByStreamToMap(List inputList) { return inputList.stream().collect(Collectors.toMap(Function.identity(), v -> 1L, Long::sum)); }

Il toMap () è un collezionista conveniente, che ci può aiutare a trasformare il flusso in differenti Mappa implementazioni.

6. Stream API Collectors.groupingBy () e Collectors.counting ()

Ad eccezione di toMap () , il nostro problema può essere risolto da altri due collezionisti, groupingBy () e counting () :

public  Map countByStreamGroupBy(List inputList) { return inputList.stream().collect(Collectors.groupingBy(k -> k, Collectors.counting())); }

L'uso corretto di Java 8 Collectors rende il nostro codice compatto e di facile lettura.

7. Conclusione

In questo rapido articolo, abbiamo illustrato vari modi per calcolare il conteggio degli elementi duplicati in un elenco.

Se desideri rispolverare lo stesso ArrayList, puoi consultare l'articolo di riferimento.

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