Java: ¿Detectar duplicados en ArrayList?

104

¿Cómo podría detectar (devolver verdadero / falso) si una ArrayList contiene más de uno del mismo elemento en Java?

Muchas gracias Terry

Editar Olvidé mencionar que no estoy buscando comparar "Bloques" entre sí, sino sus valores enteros. Cada "bloque" tiene un int y esto es lo que los hace diferentes. Encuentro el int de un Bloque en particular llamando a un método llamado "getNum" (por ejemplo, table1 [0] [2] .getNum ();


fuente
Si "Block" se compara con un int, probablemente debería hacer que hashCode devuelva el mismo int y que los iguales comparen esos int.
Paul Tomblin
use Set en lugar de List
dmarquina

Respuestas:

192

Lo más simple: volcar toda la colección en un Conjunto (usando el constructor Set (Colección) o Set.addAll), luego ver si el Conjunto tiene el mismo tamaño que ArrayList.

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

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

Actualización: si entiendo su pregunta correctamente, tiene una matriz 2d de Block, como en

Tabla de bloques [] [];

y quieres detectar si alguna fila de ellos tiene duplicados?

En ese caso, podría hacer lo siguiente, asumiendo que Block implementa "equals" y "hashCode" correctamente:

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

No estoy 100% seguro de eso para la sintaxis, por lo que sería más seguro escribirlo como

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.adddevuelve un booleano falso si el elemento que se está agregando ya está en el conjunto, por lo que incluso podría hacer un cortocircuito y descartar cualquier adición que devuelva falsesi todo lo que desea saber es si hay duplicados.

Paul Tomblin
fuente
13
Asegúrese de implementar también hashCode / equals.
jon077
1
O incluso un poco más fácil: envuélvalo al crear el conjunto, por ejemplo, nuevo HashSet (lista), en lugar de usar addAll.
Fabian Steeg
2
@ jon077: Eso depende de su definición de "duplicado".
Michael Myers
¿Sería el mismo el proceso de detección de elementos en una matriz 2D? Por ejemplo, verificando de la matriz [0] [0] a la matriz [0] [6] (una 'fila') ..? Muchas gracias, Terry
Cada objeto de la matriz tiene un valor entero. Por "duplicado", el objeto tendría el mismo valor entero.
60

Código mejorado, usando el valor de retorno de en Set#addlugar de comparar el tamaño de la lista y el conjunto.

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;
}
akuhn
fuente
7
¿Sería más eficiente decirle al HashSet cuánto espacio asignar Set<T> set = new HashSet<T>(list.size());? Dado un parámetro de lista, creo que es más eficiente si es común que la lista no contenga duplicados.
Paul Jackson
1
@PaulJackson El dimensionamiento basado en la lista completa probablemente será beneficioso. Sin embargo, si el caso común es que encuentre un duplicado antes, entonces el espacio se desperdició. Además, incluso si se ajusta el HashSettamaño de la lista, se cambiará el tamaño cuando se recorra toda la lista debido al factor de carga subyacente de la estructura hash.
Jay Anderson
1
A menos que experimente problemas reales con el tiempo de ejecución o el espacio, no afinaría su código de esa manera. Es mejor evitar la optimización prematura.
akuhn
15

Si está buscando evitar tener duplicados, entonces debe cortar el proceso intermedio de detección de duplicados y usar un Conjunto .

mate b
fuente
1
Asegúrese de implementar hashCode / equals :)
jon077
@ jon077: No necesariamente, como acabo de decir.
Michael Myers
1
Sin embargo, el uso de un conjunto no detecta duplicados. Simplemente los previene. A menos que, por supuesto, verifique el resultado del método de adición como lo indicó @akuhn arriba.
mcallahan
13

Código mejorado para devolver los elementos duplicados.

  • Puede encontrar duplicados en una colección
  • devolver el conjunto de duplicados
  • Los elementos únicos se pueden obtener del conjunto

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;
}
user60062
fuente
Eso es bastante asombroso. tiene un código inválido, y tal vez no sea la forma más óptima, ¡pero su enfoque es totalmente genial! (y funciona muy bien)
Jules Colle
9

Si sus elementos son de alguna manera Comparables (el hecho de que el orden tenga un significado real es indiferente, solo debe ser consistente con su definición de igualdad), la solución de eliminación de duplicados más rápida será ordenar la lista (0 (n log ( n))) luego hacer una sola pasada y buscar elementos repetidos (es decir, elementos iguales que se suceden) (esto es O (n)).

La complejidad general será O (n log (n)), que es aproximadamente la misma que obtendría con un Conjunto (n veces largo (n)), pero con una constante mucho menor. Esto se debe a que la constante en ordenar / deducir resulta del costo de comparar elementos, mientras que el costo del conjunto es más probable que resulte de un cálculo hash, más una (posiblemente varias) comparaciones hash. Si está utilizando una implementación de conjunto basada en hash, es decir, porque una basada en árbol le dará un O (n log² (n)), que es incluso peor.

Sin embargo, según tengo entendido, no es necesario eliminar los duplicados, sino simplemente probar su existencia. Por lo tanto, debe codificar manualmente un algoritmo de combinación o clasificación de montón en su matriz, que simplemente sale devolviendo verdadero (es decir, "hay un dup") si su comparador devuelve 0, y de lo contrario completa la clasificación, y atraviesa la prueba de matriz ordenada para repeticiones . En una combinación o clasificación de pila, de hecho, cuando se complete la clasificación, habrá comparado cada par duplicado a menos que ambos elementos ya estuvieran en sus posiciones finales (lo cual es poco probable). Por lo tanto, un algoritmo de clasificación modificado debería producir una gran mejora en el rendimiento (tendría que demostrar eso, pero supongo que el algoritmo modificado debería estar en O (log (n)) en datos uniformemente aleatorios)

Varkhan
fuente
En este caso, n es 6, por lo que no perdería mucho tiempo en los detalles de implementación, pero mantendré su idea del tipo de pila especial si alguna vez necesito hacer algo así.
Paul Tomblin
No entiendo el tercer párrafo. Mergesort y heapsort son ambos O (nlog (n)), no O (log (n)) mientras escribe; incluso si sale una vez que identifica un duplicado, eso aún no cambia la complejidad del tiempo ...
ChaimKut
8

Necesitaba hacer una operación similar para a Stream, pero no pude encontrar un buen ejemplo. Esto es lo que se me ocurrió.

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

Esto tiene la ventaja de producir un cortocircuito cuando los duplicados se encuentran temprano en lugar de tener que procesar todo el flujo y no es mucho más complicado que simplemente poner todo en un Sety verificar el tamaño. Entonces este caso sería aproximadamente:

List<T> list = ...
boolean allDistinct = areUnique(list.stream());
Jay Anderson
fuente
7

Con Java 8+ puede utilizar Stream API:

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

En pocas palabras: 1) asegúrese de que todos los elementos sean comparables 2) ordene la matriz 2) repita la matriz y encuentre duplicados

Antonio
fuente
1

Para conocer los Duplicados en una Lista use el siguiente código: Le dará el conjunto que contiene los duplicados.

 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;
  }
Rakesh Sabbani
fuente
1

La mejor manera de manejar este problema es usar 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);

Simplemente imprima la lista de matrices de resultados y vea el resultado sin duplicados :)

Ashana.Jackol
fuente
1

Si desea el conjunto de valores duplicados:

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

Y probablemente también piense en recortar valores o usar minúsculas ... según su caso.

Saurabh
fuente
La respuesta más simple y mejor si desea los duplicados, para el rendimiento, puede iniciar una sugerencia de uniqueSet con el tamaño de los argumentos.
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: esto tendrá un gran impacto en el rendimiento, ya que los elementos se eliminan del inicio de la lista. Para abordar esto, tenemos dos opciones. 1) iterar en orden inverso y eliminar elementos. 2) Utilice LinkedList en lugar de ArrayList. Debido a las preguntas sesgadas que se hacen en las entrevistas para eliminar los duplicados de la Lista sin usar ninguna otra colección, el ejemplo anterior es la respuesta. Sin embargo, en el mundo real, si tengo que lograr esto, pondré elementos de List en Set, ¡simple!

Amitesh Jha
fuente
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 ejemplo de una clase concreta que se ha anulado 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;
    }
}
faizal
fuente
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);
Venkata
fuente
Agregue una explicación con la respuesta de cómo esta respuesta ayuda a OP a solucionar el problema actual
ρяσѕρєя K
0

Esta respuesta está escrita en Kotlin, pero se puede traducir fácilmente a Java.

Si el tamaño de su lista de arrays está dentro de un rango pequeño fijo, entonces esta es una gran solución.

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
                }
            }
        }
    }
grantespo
fuente
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;
}
Ketan Ramani
fuente