Java: rilevare i duplicati in ArrayList?


104

Come posso rilevare (restituendo vero / falso) se un ArrayList contiene più di uno degli stessi elementi in Java?

Molte grazie, Terry

Modifica Ho dimenticato di dire che non sto cercando di confrontare i "blocchi" tra loro ma i loro valori interi. Ogni "blocco" ha un int e questo è ciò che li rende diversi. Trovo l'int di un blocco particolare chiamando un metodo chiamato "getNum" (ad esempio table1 [0] [2] .getNum ();


Se "Block" viene confrontato con un int, probabilmente dovresti fare in modo che hashCode restituisca lo stesso int e che sia uguale a confrontare questi int.
Paul Tomblin,

usa Set invece di List
dmarquina

Risposte:


192

Il più semplice: scarica l'intera collezione in un Set (usando il costruttore Set (Collection) o Set.addAll), quindi controlla se Set ha le stesse dimensioni di ArrayList.

List<Integer> list = ...;
Set<Integer> set = new HashSet<Integer>(list);

if(set.size() < list.size()){
    /* There are duplicates */
}

Aggiornamento: se capisco correttamente la tua domanda, hai un array 2d di Block, come in

Block table [] [];

e vuoi rilevare se qualche riga di essi ha duplicati?

In quel caso, potrei fare quanto segue, supponendo che Block implementi "uguale" e "hashCode" correttamente:

for (Block[] row : table) {
   Set set = new HashSet<Block>(); 
   for (Block cell : row) {
      set.add(cell);
   }
   if (set.size() < 6) { //has duplicate
   }
}

Non ne sono sicuro al 100% per la sintassi, quindi potrebbe essere più sicuro scriverlo come

for (int i = 0; i < 6; i++) {
   Set set = new HashSet<Block>(); 
   for (int j = 0; j < 6; j++)
    set.add(table[i][j]);
 ...

Set.addrestituisce un valore booleano falso se l'elemento che viene aggiunto è già nel set, quindi potresti persino cortocircuitare e saltare qualsiasi aggiunta che ritorni falsese tutto ciò che vuoi sapere è se ci sono duplicati.


13
Assicurati di implementare anche hashCode / equals.
jon077

1
O anche un po 'più semplice: avvolgilo durante la creazione del set, ad esempio new HashSet (list), invece di usare addAll.
Fabian Steeg

2
@ jon077: Dipende dalla tua definizione di "duplicato".
Michael Myers

Il processo di rilevamento degli elementi in un array 2D sarebbe lo stesso? Ad esempio, controllando da array [0] [0] a array [0] [6] (una 'riga') ..? Molte grazie, Terry

Ogni oggetto nell'array contiene un valore intero. Con "duplicato", l'oggetto avrebbe lo stesso valore intero.

60

Codice migliorato, utilizzando il valore restituito Set#addinvece di confrontare la dimensione di elenco e set.

public static <T> boolean hasDuplicate(Iterable<T> all) {
    Set<T> set = new HashSet<T>();
    // Set#add returns false if the set does not change, which
    // indicates that a duplicate element has been added.
    for (T each: all) if (!set.add(each)) return true;
    return false;
}

7
Sarebbe più efficace per raccontare la HashSet quanto spazio allocare: Set<T> set = new HashSet<T>(list.size());? Dato un parametro List, penso che sia più efficiente se è comune che l'elenco non contenga duplicati.
Paul Jackson

1
@PaulJackson Il dimensionamento basato sull'elenco completo sarà probabilmente vantaggioso. Tuttavia, se il caso comune è trovare un duplicato in anticipo, lo spazio è stato sprecato. Anche il ridimensionamento della HashSetalla dimensione dell'elenco risulterà in un ridimensionamento durante l'esecuzione dell'intero elenco a causa del fattore di caricamento sottostante della struttura hash.
Jay Anderson

1
A meno che tu non abbia problemi reali con il runtime o lo spazio, non metterei a punto il tuo codice in questo modo. È meglio evitare l'ottimizzazione prematura.
akuhn

15

Se stai cercando di evitare del tutto di avere duplicati, dovresti semplicemente eliminare il processo intermedio di rilevamento dei duplicati e utilizzare un Set .


1
Assicurati di implementare hashCode / equals :)
jon077

@ jon077: Non necessariamente, come ho appena detto.
Michael Myers

1
Tuttavia, l'utilizzo di un set non rileva i duplicati. Li impedisce solo. A meno che, ovviamente, non controlli il risultato del metodo di aggiunta come indicato da @akuhn sopra.
mcallahan

13

Codice migliorato per restituire gli elementi duplicati

  • Può trovare duplicati in una raccolta
  • restituisce la serie di duplicati
  • Gli elementi unici possono essere ottenuti dal set

public static <T> List getDuplicate(Collection<T> list) {

    final List<T> duplicatedObjects = new ArrayList<T>();
    Set<T> set = new HashSet<T>() {
    @Override
    public boolean add(T e) {
        if (contains(e)) {
            duplicatedObjects.add(e);
        }
        return super.add(e);
    }
    };
   for (T t : list) {
        set.add(t);
    }
    return duplicatedObjects;
}


public static <T> boolean hasDuplicate(Collection<T> list) {
    if (getDuplicate(list).isEmpty())
        return false;
    return true;
}

È davvero fantastico. hai un codice non valido, e forse non è il modo più ottimale, ma il tuo approccio è assolutamente eccezionale! (e funziona alla grande)
Jules Colle

9

Se i tuoi elementi sono in qualche modo Comparabili (il fatto che l'ordine abbia un significato reale è indifferente: deve solo essere coerente con la tua definizione di uguaglianza), la soluzione di rimozione dei duplicati più veloce ordinerà l'elenco (0 (n log ( n))) quindi eseguire un singolo passaggio e cercare elementi ripetuti (ovvero elementi uguali che si susseguono) (questo è O (n)).

La complessità complessiva sarà O (n log (n)), che è più o meno la stessa di quella che otterresti con un Set (n volte lungo (n)), ma con una costante molto più piccola. Questo perché la costante nell'ordinamento / deduplicazione risulta dal costo del confronto degli elementi, mentre è molto probabile che il costo dell'insieme derivi da un calcolo hash, più uno (forse diversi) confronti hash. Se stai usando un'implementazione Set basata su hash, cioè perché un Tree based ti darà un O (n log² (n)), che è anche peggio.

A quanto mi risulta, tuttavia, non è necessario rimuovere i duplicati, ma semplicemente testarne l'esistenza. Quindi dovresti codificare manualmente un algoritmo di unione o di ordinamento dell'heap sul tuo array, che semplicemente esce restituendo true (cioè "c'è un dup") se il tuo comparatore restituisce 0, e altrimenti completa l'ordinamento e attraversa l'array ordinato testando le ripetizioni . In un ordinamento di unione o di heap, infatti, quando l'ordinamento è completato, avrai confrontato ogni coppia duplicata a meno che entrambi gli elementi non fossero già nelle loro posizioni finali (il che è improbabile). Pertanto, un algoritmo di ordinamento ottimizzato dovrebbe produrre un enorme miglioramento delle prestazioni (dovrei dimostrarlo, ma immagino che l'algoritmo ottimizzato dovrebbe essere in O (log (n)) su dati uniformemente casuali)


In questo caso, n è 6 quindi non sprecherei molto tempo sui dettagli di implementazione, ma manterrò la tua idea dello speciale ordinamento di heap se avessi bisogno di fare qualcosa del genere.
Paul Tomblin,

Non capisco il terzo paragrafo. Mergesort e heapsort sono entrambi O (nlog (n)), non O (log (n)) mentre scrivi; anche se esci una volta identificato un duplicato, ciò non cambia la tua complessità temporale ...
ChaimKut

8

Avevo bisogno di eseguire un'operazione simile per a Stream, ma non sono riuscito a trovare un buon esempio. Ecco cosa mi è venuto in mente.

public static <T> boolean areUnique(final Stream<T> stream) {
    final Set<T> seen = new HashSet<>();
    return stream.allMatch(seen::add);
}

Questo ha il vantaggio di cortocircuitare quando i duplicati vengono trovati in anticipo piuttosto che dover elaborare l'intero flusso e non è molto più complicato che mettere tutto in a Sete controllare le dimensioni. Quindi questo caso sarebbe approssimativamente:

List<T> list = ...
boolean allDistinct = areUnique(list.stream());

7

Con Java 8+ puoi utilizzare Stream API:

boolean areAllDistinct(List<Block> blocksList) {
    return blocksList.stream().map(Block::getNum).distinct().count() == blockList.size();
}

2

In poche parole: 1) assicurati che tutti gli elementi siano confrontabili 2) ordina l'array 2) itera sull'array e trova i duplicati


1

Per conoscere i duplicati in una lista usa il seguente codice: Ti darà il set che contiene i duplicati.

 public Set<?> findDuplicatesInList(List<?> beanList) {
    System.out.println("findDuplicatesInList::"+beanList);
    Set<Object> duplicateRowSet=null;
    duplicateRowSet=new LinkedHashSet<Object>();
            for(int i=0;i<beanList.size();i++){
                Object superString=beanList.get(i);
                System.out.println("findDuplicatesInList::superString::"+superString);
                for(int j=0;j<beanList.size();j++){
                    if(i!=j){
                         Object subString=beanList.get(j);
                         System.out.println("findDuplicatesInList::subString::"+subString);
                         if(superString.equals(subString)){
                             duplicateRowSet.add(beanList.get(j));
                         }
                    }
                }
            }
            System.out.println("findDuplicatesInList::duplicationSet::"+duplicateRowSet);
        return duplicateRowSet;
  }

1

Il modo migliore per gestire questo problema è utilizzare un HashSet :

ArrayList<String> listGroupCode = new ArrayList<>();
listGroupCode.add("A");
listGroupCode.add("A");
listGroupCode.add("B");
listGroupCode.add("C");
HashSet<String> set = new HashSet<>(listGroupCode);
ArrayList<String> result = new ArrayList<>(set);

Basta stampare l' elenco dei risultati e vedere il risultato senza duplicati :)


1

Se vuoi l'insieme di valori duplicati:

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class FindDuplicateInArrayList {

    public static void main(String[] args) {

        Set<String> uniqueSet = new HashSet<String>();
        List<String> dupesList = new ArrayList<String>();
        for (String a : args) {
            if (uniqueSet.contains(a))
                dupesList.add(a);
            else
                uniqueSet.add(a);
        }
        System.out.println(uniqueSet.size() + " distinct words: " + uniqueSet);
        System.out.println(dupesList.size() + " dupesList words: " + dupesList);
    }
}

E probabilmente pensa anche a tagliare i valori o usare le lettere minuscole ... a seconda del tuo caso.


La risposta più semplice e migliore se vuoi i duplicati, per le prestazioni puoi avviare un suggerimento uniqueSet con la dimensione degli argomenti.
Christophe Roussy,

0
    String tempVal = null;
    for (int i = 0; i < l.size(); i++) {
        tempVal = l.get(i); //take the ith object out of list
        while (l.contains(tempVal)) {
            l.remove(tempVal); //remove all matching entries
        }
        l.add(tempVal); //at last add one entry
    }

Nota: questo avrà un notevole impatto sulle prestazioni anche se gli elementi vengono rimossi dall'inizio dell'elenco. Per risolvere questo problema, abbiamo due opzioni. 1) iterare in ordine inverso e rimuovere gli elementi. 2) Usa LinkedList invece di ArrayList. A causa delle domande distorte poste nelle interviste per rimuovere i duplicati dall'elenco senza utilizzare altre raccolte, l'esempio sopra è la risposta. Nel mondo reale, però, se devo raggiungere questo obiettivo, inserirò elementi da List a Set, semplice!


0
/**
     * Method to detect presence of duplicates in a generic list. 
     * Depends on the equals method of the concrete type. make sure to override it as required.
     */
    public static <T> boolean hasDuplicates(List<T> list){
        int count = list.size();
        T t1,t2;

        for(int i=0;i<count;i++){
            t1 = list.get(i);
            for(int j=i+1;j<count;j++){
                t2 = list.get(j);
                if(t2.equals(t1)){
                    return true;
                }
            }
        }
        return false;
    }

Un esempio di una classe concreta che ha sovrascritto equals():

public class Reminder{
    private long id;
    private int hour;
    private int minute;

    public Reminder(long id, int hour, int minute){
        this.id = id;
        this.hour = hour;
        this.minute = minute;
    }

    @Override
    public boolean equals(Object other){
        if(other == null) return false;
        if(this.getClass() != other.getClass()) return false;
        Reminder otherReminder = (Reminder) other;
        if(this.hour != otherReminder.hour) return false;
        if(this.minute != otherReminder.minute) return false;

        return true;
    }
}

0
    ArrayList<String> withDuplicates = new ArrayList<>();
    withDuplicates.add("1");
    withDuplicates.add("2");
    withDuplicates.add("1");
    withDuplicates.add("3");
    HashSet<String> set = new HashSet<>(withDuplicates);
    ArrayList<String> withoutDupicates = new ArrayList<>(set);

    ArrayList<String> duplicates = new ArrayList<String>();

    Iterator<String> dupIter = withDuplicates.iterator();
    while(dupIter.hasNext())
    {
    String dupWord = dupIter.next();
    if(withDuplicates.contains(dupWord))
    {
        duplicates.add(dupWord);
    }else{
        withoutDupicates.add(dupWord);
    }
    }
  System.out.println(duplicates);
  System.out.println(withoutDupicates);

Aggiungi qualche spiegazione con la risposta su come questa risposta aiuti OP a risolvere il problema attuale
ρяσsρєя K

0

Questa risposta è scritta in Kotlin, ma può essere facilmente tradotta in Java.

Se la dimensione del tuo arraylist rientra in un piccolo intervallo fisso, questa è un'ottima soluzione.

var duplicateDetected = false
    if(arrList.size > 1){
        for(i in 0 until arrList.size){
            for(j in 0 until arrList.size){
                if(i != j && arrList.get(i) == arrList.get(j)){
                    duplicateDetected = true
                }
            }
        }
    }

0
private boolean isDuplicate() {
    for (int i = 0; i < arrayList.size(); i++) {
        for (int j = i + 1; j < arrayList.size(); j++) {
            if (arrayList.get(i).getName().trim().equalsIgnoreCase(arrayList.get(j).getName().trim())) {
                return true;
            }
        }
    }

    return false;
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.