2008-11-06 17 views
72

Sé acerca de SortedSet, pero en mi caso necesito algo que implemente List, y no Set. Entonces, ¿hay una implementación por ahí, en la API o en otro lugar?¿Hay una implementación de lista sin duplicados por ahí?

No debería ser difícil de implementar, pero pensé por qué no preguntarle a la gente aquí primero?

+0

¿Por qué es necesario implementar List? Los conjuntos son iterables, al igual que las listas, por lo que supongo que el método de recepción está aplicando la Lista por algún otro motivo. – Rob

+0

@Rob Así es, es una demanda externa, y la estructura de datos incluye mucho más de una lista. – Yuval

+0

Si el usuario quiere una LISTA, está claro que necesita métodos de la interfaz LIST que no estén presentes en la interfaz SET ... – marcolopes

Respuesta

75

No hay colección de Java en la biblioteca estándar para hacer esto. LinkedHashSet<E> conserva el pedido de forma similar a List, por lo que si envuelve su conjunto en List cuando desea usarlo como List obtendrá la semántica que desee.

Alternativamente, el Commons Collections (o commons-collections4, para la versión genérica) tiene un List el que hace lo que quiere ya: SetUniqueList/SetUniqueList<E>.

+5

La clase de los Comunes es exactamente lo que necesito, pero mi jefe me dijo que lo implementara eventualmente. 10x de todos modos! – Yuval

+3

¡Ah, nada como reinventar la rueda! Ahora sabrá si vuelve a surgir la necesidad, de todos modos. collections15 es una cosa bastante útil para dar vueltas; MultiMaps en particular alivia el dolor de algo en que uno termina implementándose mucho. – Calum

+19

@skaffman: en realidad no es un idiota, pero a veces hace movimientos que son ... bueno, extraños. De todos modos, no voy a introducir errores en el producto. En el mercado de hoy, estoy contento con mi trabajo y no estoy buscando cerrar puertas y quemar puentes, si entiendes mi punto. – Yuval

4

Por qué no encapsular un conjunto con una lista, más o menos como:

new ArrayList(new LinkedHashSet()) 

Esto deja a la otra aplicación para alguien que es un verdadero maestro de Colecciones ;-)

+2

Este constructor copia el contenido del conjunto en la nueva lista, en lugar de envolverlo. – Calum

+0

@Calum, eso es correcto, pero en lugar de preocuparse por no agregar duplicados a una lista, puede agregar sus objetos a un conjunto (y dejar que el conjunto se preocupe por filtrar los duplicados) y simplemente envolver ese conjunto en una lista al pasarlo al método externo. –

+3

Esto copia un conjunto a una lista pero no tiene ningún pedido conocido. Pero de esto se trata la pregunta. – Janning

0

El documentation for collection interfaces dice:

Conjunto: una colección que no puede contener elementos duplicados.
Lista: una colección ordenada (a veces llamada secuencia). Las listas pueden contener elementos duplicados.

Así que si no quiere duplicados, probablemente no debería usar una lista.

+0

mencioné específicamente que necesito una implementación de lista. Confía en mí, hay una razón. – Yuval

+0

¿Es el motivo porque estás interactuando con una API que toma una lista como parámetro (en lugar de una colección)? Eso es un poco molesto tener que lidiar con –

+0

En realidad, la API toma un Mapa >>, lo que significa mantener en algún lugar cerca de docenas a cientos de listas ... bah. – Yuval

0

En la parte superior de mi cabeza, las listas permiten duplicados. Puede implementar rápidamente un UniqueArrayList y anular todas las funciones add/insert para verificar antes de llamar a los métodos heredados. Para uso personal, solo puede implementar el método add que utiliza, y anular los demás para lanzar una excepción en caso de que los programadores futuros intenten usar la lista de una manera diferente.

+0

Estaba listo para recurrir a esta idea (que finalmente tuve que) si nadie sugirió algo mejor = 8-) Ver mi propia respuesta anterior. – Yuval

11

Así que esto es lo que hice eventualmente. Espero que esto ayude a alguien más.

class NoDuplicatesList<E> extends LinkedList<E> { 
    @Override 
    public boolean add(E e) { 
     if (this.contains(e)) { 
      return false; 
     } 
     else { 
      return super.add(e); 
     } 
    } 

    @Override 
    public boolean addAll(Collection<? extends E> collection) { 
     Collection<E> copy = new LinkedList<E>(collection); 
     copy.removeAll(this); 
     return super.addAll(copy); 
    } 

    @Override 
    public boolean addAll(int index, Collection<? extends E> collection) { 
     Collection<E> copy = new LinkedList<E>(collection); 
     copy.removeAll(this); 
     return super.addAll(index, copy); 
    } 

    @Override 
    public void add(int index, E element) { 
     if (this.contains(element)) { 
      return; 
     } 
     else { 
      super.add(index, element); 
     } 
    } 
} 
+8

Tenga cuidado: LinkedList.contains() necesita escanear toda la lista para determinar si un objeto está contenido en la lista. Esto significa que cuando agrega objetos a una Lista grande, se escanea toda la Lista para cada operación de adición (en el peor de los casos). Esto puede terminar siendo LENTO. –

+7

Además, su anulación de addAll no comprueba si hay duplicados en la colección que se pasa a addAll(). –

5

Usted debería considerar seriamente la respuesta de dhiller:

  1. lugar de preocuparse por la adición de sus objetos a una lista de duplicados menos, agregarlos a un conjunto (cualquier aplicación), el cual por el filtro de la naturaleza cabo los duplicados
  2. Cuando necesite llamar al método que requiere una lista, envuélvalo en un new ArrayList(set) (o un new LinkedList(set), lo que sea).

creo que la solución informado con la NoDuplicatesList tiene algunos problemas, sobre todo con el método , además de su clase no se ocupa de la comprobación de duplicados en la Colección pasados ​​a su método de addAll().

+0

Me encantaría aprender sobre estos problemas de contains(). En cuanto a addAll(), creo una copia de la colección dada y elimino todos los objetos que ya están en 'esto'. ¿Cómo eso no maneja duplicados? – Yuval

+0

Como mencioné en mi comentario a la publicación de su clase, contiene() tiene que escanear toda la lista (en el peor de los casos) para encontrar si el objeto está en la lista. Si tiene una lista de 1 millón de elementos y añádalos 10 por separado, entonces (en el peor de los casos) se escanean más de diez millones de elementos. –

+0

En cuanto a addAll(), si la recopilación pasada a addAll contiene duplicados, no se detectan. Por ejemplo: su lista de parámetros {A, B, C, D} {B, D, E, E, E}. Usted crea una copia del parámetro, y después de eliminarToda ella contiene {E, E, E}. –

2

Necesitaba algo así, así que fui a las colecciones de commons y utilicé SetUniqueList, pero cuando realicé una prueba de rendimiento, encontré que no parece optimizado en comparación con el caso si quiero usar un conjunto y obtener una matriz que utiliza el método Set.toArray(), el SetUniqueTest tardó 20: 1 tiempo en completarse y luego recorrer 100.000 Strings en comparación con la otra implementación, lo cual es una gran diferencia, por lo que si te preocupa el rendimiento, te recomiendo utilice el conjunto y obtener una matriz en lugar de utilizar el SetUniqueList, a menos que realmente necesita la lógica de la SetUniqueList, entonces usted necesita para comprobar otras soluciones ...

el método principal de código de prueba:

public void main (String [] args) estáticas {

SetUniqueList pq = SetUniqueList.decorate(new ArrayList()); 
Set s = new TreeSet(); 

long t1 = 0L; 
long t2 = 0L; 
String t; 


t1 = System.nanoTime(); 
for (int i = 0; i < 200000; i++) { 
    pq.add("a" + Math.random()); 
} 
while (!pq.isEmpty()) { 
    t = (String) pq.remove(0); 
} 
t1 = System.nanoTime() - t1; 

t2 = System.nanoTime(); 
for (int i = 0; i < 200000; i++) { 
    s.add("a" + Math.random()); 
} 

s.clear(); 
String[] d = (String[]) s.toArray(new String[0]); 
s.clear(); 
for (int i = 0; i < d.length; i++) { 
    t = d[i]; 

} 
t2 = System.nanoTime() - t2; 

System.out.println((double)t1/1000/1000/1000); //seconds 
System.out.println((double)t2/1000/1000/1000); //seconds 
System.out.println(((double) t1)/t2);  //comparing results 

}

Saludos Mohammed Sleem http://abusleem.net/blog

-3

acabo de hacer mi propia UniqueList en mi propia pequeña biblioteca como esta:

package com.bprog.collections;//my own little set of useful utilities and classes 

import java.util.HashSet; 
import java.util.ArrayList; 
import java.util.List; 
/** 
* 
* @author Jonathan 
*/ 
public class UniqueList { 

private HashSet masterSet = new HashSet(); 
private ArrayList growableUniques; 
private Object[] returnable; 

public UniqueList() { 
    growableUniques = new ArrayList(); 
} 

public UniqueList(int size) { 
    growableUniques = new ArrayList(size); 
} 

public void add(Object thing) { 
    if (!masterSet.contains(thing)) { 
     masterSet.add(thing); 
     growableUniques.add(thing); 
    } 
} 

/** 
* Casts to an ArrayList of unique values 
* @return 
*/ 
public List getList(){ 
    return growableUniques; 
} 

public Object get(int index) { 
    return growableUniques.get(index); 
} 

public Object[] toObjectArray() { 
    int size = growableUniques.size(); 
    returnable = new Object[size]; 
    for (int i = 0; i < size; i++) { 
     returnable[i] = growableUniques.get(i); 
    } 
    return returnable; 
    } 
} 

Tengo una clase TestCollections que se ve así:

package com.bprog.collections; 
import com.bprog.out.Out; 
/** 
* 
* @author Jonathan 
*/ 
public class TestCollections { 
    public static void main(String[] args){ 
     UniqueList ul = new UniqueList(); 
     ul.add("Test"); 
     ul.add("Test"); 
     ul.add("Not a copy"); 
     ul.add("Test"); 
     //should only contain two things 
     Object[] content = ul.toObjectArray(); 
     Out.pl("Array Content",content); 
    } 
} 

Funciona bien. Todo lo que hace es agregar a un conjunto si no lo tiene ya y hay un Arraylist que es retornable, así como una matriz de objetos.

+2

Esto no implementa la Lista – Eva

+0

Sí, debe agregarle un poco más de métodos para implementar la interfaz de la Lista. – gyurix

0

en el método add, ¿por qué no utilizar HashSet.add() para verificar duplicados en lugar de HashSet.consist(). HashSet.add() devolverá true si no hay duplicados y false en caso contrario.

+0

¿Qué es 'HashSet # consisten()'? – naXa

9

Esto es lo que hice y funciona.

Suponiendo que tengo un ArrayList para trabajar, lo primero que hice fue crear un nuevo LinkedHashMap.

LinkedHashSet<E> hashSet = new LinkedHashSet<E>() 

Luego intento agregar mi nuevo elemento al LinkedHashSet. El método add no altera el LinkedHasSet y devuelve falso si el nuevo elemento es un duplicado. Así que esto se convierte en una condición que puedo probar antes de agregar al ArrayList.

if (hashSet.add(E)) arrayList.add(E); 

Esta es una manera simple y elegante de evitar que se agreguen duplicados a una lista de matriz. Si lo desea, puede encapsularlo y anular el método add en una clase que amplía el ArrayList. Simplemente recuerde tratar con addAll al recorrer los elementos y llamar al método add.

+1

Sí, creo que esta es la mejor solución, también puedes usar un HashSet normal, no un Linked, y luego puedes usar tu lista como quieras, también puedes decidir qué hacer en algunas situaciones, como al agregar un elemento dentro de una lista antes de un índice específico, puede desear que desee mover el elemento duplicado a esta posición o no. – gyurix

+0

La mejor solución aquí ...Publicaré mi código de clase UniqueList – marcolopes

+0

Esto funcionó para mí, en mi algoritmo BFS Graph. Porque tenía algunos nodos que agregué a una cola (LinkedList) solo si no estaban ya incluidos. –

0

NOTA: no se necesita subList implementación.

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

public class UniqueList<T> extends ArrayList<T> { 

    private static final long serialVersionUID = 1L; 

    /** Unique elements SET */ 
    private final Set<T> set=new HashSet(); 

    /** Used by addAll methods */ 
    private Collection<T> addUnique(Collection<? extends T> col) { 
     Collection<T> unique=new ArrayList(); 
     for(T e: col){ 
      if (set.add(e)) unique.add(e); 
     } 
     return unique; 
    } 

    @Override 
    public boolean add(T e) { 
     return set.add(e) ? super.add(e) : false; 
    } 

    @Override 
    public boolean addAll(Collection<? extends T> col) { 
     return super.addAll(addUnique(col)); 
    } 

    @Override 
    public void add(int index, T e) { 
     if (set.add(e)) super.add(index, e); 
    } 

    @Override 
    public boolean addAll(int index, Collection<? extends T> col) { 
     return super.addAll(index, addUnique(col)); 
    } 

} 
Cuestiones relacionadas