2012-05-25 15 views
10

Esiste uno strumento o una libreria per trovare voci duplicate in una raccolta in base a criteri specifici che possono essere implementati?Ricerca di voci duplicate nella raccolta


Per rendermi chiaro: voglio confrontare le voci tra loro in base a criteri specifici. Quindi penso che un Predicate restituendo solo true o false non sia sufficiente.


Non riesco a utilizzare equals.

+1

In che modo si desidera specificare i criteri di deduplicazione? Come un predicato binario? – NPE

+1

Vuoi * trovare * i duplicati o * rimuoverli *? –

+0

@ AndyThomas-Cramer In realtà sarebbe sufficiente solo sapere se ci sono duplicati. –

risposta

2

Ho creato una nuova interfaccia simile all'interfaccia IEqualityComparer<T> in .NET.

Tale EqualityComparator<T> Quindi passare al seguente metodo che rileva i duplicati.

public static <T> boolean hasDuplicates(Collection<T> collection, 
     EqualsComparator<T> equalsComparator) { 
    List<T> list = new ArrayList<>(collection); 
    for (int i = 0; i < list.size(); i++) { 
     T object1 = list.get(i); 
     for (int j = (i + 1); j < list.size(); j++) { 
      T object2 = list.get(j); 
      if (object1 == object2 
        || equalsComparator.equals(object1, object2)) { 
       return true; 
      } 
     } 
    } 
    return false; 
} 

In questo modo posso personalizzare il confronto con le mie esigenze.

2

È possibile utilizzare una mappa e durante l'iterazione della raccolta inserire gli elementi nella mappa (i predicati formerebbero la chiave) e se c'è già una voce è stato trovato un duplicato.

Per ulteriori informazioni si veda qui: Finding duplicates in a collection

7

Dipende dalla semantica del criterio:

Se il criterio è sempre lo stesso per una determinata classe, ed è inerente il concetto sottostante, dovresti semplicemente implementare equals e hashCode e utilizzare un set.

Se il criterio dipende dal contesto, org.apache.commons.collections.CollectionUtils.select(java.util.Collection, org.apache.commons.collections.Predicate) potrebbe essere la soluzione giusta per voi.

+0

Voglio confrontare le voci tra di loro, non con criteri arbitrari. –

4

Se si vuole trovare duplicati, e non solo la loro rimozione, un approccio sarebbe quello di lanciare la collezione in un array, ordinare l'array tramite un comparatore che implementa i criteri di, quindi linearmente a piedi attraverso l'array, guardando per duplicati adiacenti.

Ecco uno schizzo (non testato):

MyComparator myComparator = new MyComparator(); 
    MyType[] myArray = myList.toArray(); 
    Arrays.sort(myArray, myComparator); 
    for (int i = 1; i < myArray.length; ++i) { 
     if (0 == myComparator.compare(myArray[i - 1], myArray[i])) { 
     // Found a duplicate! 
     } 
    } 

Edit: Dal tuo commento, si vuole solo sapere se ci sono duplicati. L'approccio sopra funziona anche per questo. Ma potresti semplicemente creare un java.util.SortedSet con un comparatore personalizzato. Ecco uno schizzo:

MyComparator myComparator = new MyComparator(); 
    TreeSet treeSet = new TreeSet(myComparator); 
    treeSet.addAll(myCollection); 
    boolean containsDuplicates = (treeSet.size() != myCollection.size()); 
3

È possibile adattare una serie Java per la ricerca di duplicati tra gli oggetti di un tipo arbitrario: avvolgere la classe di destinazione in un involucro privato che valuta l'uguaglianza, sulla base di criteri, e costruire una serie di involucri .

Ecco un esempio un po 'lungo che illustra la tecnica. Considera che due persone con lo stesso nome sono uguali e quindi rileva tre duplicati nella matrice di cinque oggetti.

import java.util.*; 
import java.lang.*; 

class Main { 
    static class Person { 
     private String first; 
     private String last; 
     public String getFirst() {return first;} 
     public String getLast() {return last;} 
     public Person(String f, String l) { 
      first = f; 
      last = l; 
     } 
     public String toString() { 
      return first+" "+last; 
     } 
    } 
    public static void main (String[] args) throws java.lang.Exception { 
     List<Person> people = new ArrayList<Person>(); 
     people.add(new Person("John", "Smith")); 
     people.add(new Person("John", "Scott")); 
     people.add(new Person("Jack", "First")); 
     people.add(new Person("John", "Walker")); 
     people.add(new Person("Jack", "Black")); 
     Set<Object> seen = new HashSet<Object>(); 
     for (Person p : people) { 
      final Person thisPerson = p; 
      class Wrap { 
       public int hashCode() { return thisPerson.getFirst().hashCode(); } 
       public boolean equals(Object o) { 
        Wrap other = (Wrap)o; 
        return other.wrapped().getFirst().equals(thisPerson.getFirst()); 
       } 
       public Person wrapped() { return thisPerson; } 
      }; 
      Wrap wrap = new Wrap(); 
      if (seen.add(wrap)) { 
       System.out.println(p + " is new"); 
      } else { 
       System.out.println(p + " is a duplicate"); 
      } 
     } 
    } 
} 

È possibile giocare con questo esempio su ideone [link].

+0

+1: interessante! Non ho idea dell'efficienza. – dragon66

+0

@ dragon66 Se la tua funzione di hash è buona, l'efficienza è la stessa di qualsiasi tabella di hash, che è 'O (1)' per ogni elemento, o 'O (N)' per l'intera collezione. – dasblinkenlight

+0

dasblinkenlight: Sono un po 'preoccupato per la creazione dell'oggetto wrap anche se so che saranno andati fuori dal giro. – dragon66

-2

Iterate il ArrayList che contiene duplicati e aggiungeteli allo HashSet. Quando il metodo add restituisce false nello HashSet, è sufficiente registrare il duplicato nella console.

+1

Come dice l'OP, non può usare 'equals()'. Un 'HashSet' usa' hashCode() 'e' equals() '. Pertanto non può usare un 'HashSet'. –

0

TreeSet permette di fare facilmente questo:

Set uniqueItems = new TreeSet<>(yourComparator); 
List<?> duplicates = objects.stream().filter(o -> !uniqueItems.add(o)).collect(Collectors.toList()); 

yourComarator viene usato quando si chiama uniqueItems.add(o), che aggiunge la voce al set e restituisce true se l'articolo è unico nel suo genere. Se il comparatore considera duplicato l'articolo, add(o) restituirà false.

Si noti che il metodo equals dell'articolo deve essere coerente con yourComarator come da the TreeSet documentation affinché funzioni.

Problemi correlati