¿Alguna implementación de Ordered Set en Java?

98

Si alguien está familiarizado con Objective-C, hay una colección llamada NSOrderedSetque actúa como Set y se puede acceder a sus elementos como los de un Array .

¿Hay algo parecido a esto en Java?

Escuché que hay una colección llamada LinkedHashMap, pero no he encontrado nada parecido para un set.

Uko
fuente
Estoy trabajando en un problema similar en c ++. con NSOrderedSet, ¿podemos acceder a los elementos en el orden en que los insertamos?
Vinay
¿Sabes cómo obtener la funcionalidad anterior en C ++? i, e actuando como SET y se puede acceder como elementos de una matriz?
Vinay

Respuestas:

118

Eche un vistazo a la clase LinkedHashSet

Desde el documento de Java :

Implementación de tabla hash y lista vinculada de la interfaz Set, con orden de iteración predecible . Esta implementación se diferencia de HashSet en que mantiene una lista doblemente enlazada que se ejecuta en todas sus entradas. Esta lista enlazada define el orden de iteración, que es el orden en el que se insertaron los elementos en el conjunto (orden de inserción) . Tenga en cuenta que el orden de inserción no se ve afectado si un elemento se vuelve a insertar en el conjunto . (Un elemento e se vuelve a insertar en un conjunto s si se invoca s.add (e) cuando s.contains (e) devolvería verdadero inmediatamente antes de la invocación).

Chandra Sekhar
fuente
Muchas gracias. Parece trivial mirarlo, LinkedHashMappero no lo he encontrado de alguna manera.
Uko
3
Clase LinkedHashSet <E>
Chandra Sekhar
9
¿Por qué esta respuesta recibe tantos votos a favor? Esta no es una respuesta a la pregunta, en absoluto. No hay ninguna función LinkedHashSetque le permita averiguar en qué índice también se encuentra el elemento.
motor de búsqueda 27 de
31

Cada conjunto tiene un iterador (). El iterador de un HashSet normal es bastante aleatorio, un TreeSet lo hace por orden de clasificación, un iterador LinkedHashSet itera por orden de inserción.

Sin embargo, no puede reemplazar un elemento en un LinkedHashSet. Puede eliminar uno y agregar otro, pero el nuevo elemento no estará en el lugar del original. En un LinkedHashMap, puede reemplazar un valor de una clave existente, y luego los valores seguirán en el orden original.

Además, no puede insertar en una posición determinada.

Tal vez sea mejor que use una ArrayList con una verificación explícita para evitar insertar duplicados.

GreyFairer
fuente
Quiero poder establecer / obtener elementos en una posición específica y obtenerlos por orden que los agregué. Parece que LinkedHashSetdebería hacer eso. Gracias por la respuesta
Uko
11

Eche un vistazo al documento API estándar de Java . Justo al lado LinkedHashMap, hay un LinkedHashSet. Pero tenga en cuenta que el orden en esos es el orden de inserción, no el orden natural de los elementos. Y solo puede iterar en ese orden, no hacer acceso aleatorio (excepto contando los pasos de iteración).

También hay una interfaz SortedSetimplementada por TreeSety ConcurrentSkipListSet. Ambos permiten la iteración en el orden natural de sus elementos o un Comparator, pero no el acceso aleatorio o el orden de inserción.

Para una estructura de datos que tenga acceso eficiente por índice y pueda implementar de manera eficiente el criterio establecido, necesitaría una lista de omisión , pero no hay implementación con esa funcionalidad en la API estándar de Java, aunque estoy seguro de que es fácil encontrar una. En Internet.

Michael Borgwardt
fuente
Puede que esté malinterpretando su comentario, pero tenía la impresión de que desde Java 1.6 había varias colecciones predeterminadas basadas en listas de omisión (como, por ejemplo, ConcurrentSkipListSet , etc.).
TacticalCoder
@ user988052: sí, pero esos no implementan el acceso aleatorio por índice (aunque mi comprensión de las listas de omisión dice que debería ser posible), que parece ser lo que quiere Uko.
Michael Borgwardt
@MichaelBorgwardt Java 6 y versiones posteriores incluyen un par de implementaciones de lista de omisión: ConcurrentSkipListMapy ConcurrentSkipListSet. Ambos mantienen una ordenación basada en el orden natural o un Comparador. No entiendo si proporcionan el acceso aleatorio o el orden de entrada que comenta.
Basil Bourque
@BasilBourque: buen hallazgo y gracias por las ediciones. OP quería acceso por índice, y ahora que lo he mirado y lo pienso, creo que las listas de omisión tampoco tienen esa capacidad ...
Michael Borgwardt
5

TreeSet está ordenado.

http://docs.oracle.com/javase/6/docs/api/java/util/TreeSet.html

Mike Yockey
fuente
Esta es la respuesta correcta. A diferencia de LHSet, TreeSet no implementar java.util.SortedSet.
vemv
40
ordenado y clasificado son cosas diferentes. TreeSet está ordenado, no ordenado
andrii
2
Exactamente, ordenado se refiere al orden de inserción (la forma en que funciona una Lista), mientras que ordenado se refiere al orden posterior de los elementos según algunos criterios.
Cornel Masson
5

Intente usar java.util.TreeSetesos implementos SortedSet.

Para citar el doc:

"Los elementos se ordenan usando su orden natural, o por un Comparador proporcionado en el momento de creación establecido, dependiendo del constructor que se use"

Tenga en cuenta que agregar, quitar y contiene tiene un registro de costo de tiempo (n).

Si desea acceder al contenido del conjunto como una matriz, puede convertirlo haciendo:

YourType[] array = someSet.toArray(new YourType[yourSet.size()]); 

Esta matriz se ordenará con los mismos criterios que el TreeSet (natural o por un comparador), y en muchos casos esto tendrá una ventaja en lugar de hacer un Arrays.sort ()

Thomas Johan Eggum
fuente
1
Necesito pedir al igual que en ei ArrayList si pongo primer elemento cy luego elemento a, ya que iterar sobre una colección que quiero conseguir en el mismo orden: c, aetc
Uko
1

treeset es un conjunto ordenado, pero no puede acceder a través de un índice de elementos, simplemente iterar o ir al principio / final.

NimChimpsky
fuente
Con treeSet incurrirá en un mayor costo. LinkedHashSet tiene un costo menor.
Carlos
0

Si estamos hablando de una implementación económica de la lista de omisión, me pregunto en términos de gran O, cuál es el costo de esta operación:

YourType [] array = someSet.toArray (nuevo YourType [yourSet.size ()]);

Quiero decir que siempre se queda atascado en una creación de matriz completa, por lo que es O (n):

java.util.Arrays#copyOf
acritud
fuente
1
Eso depende de las características de rendimiento del iterador y del size()método del conjunto subyacente. La iteración suele ser O(n), el tamaño suele ser O(1)excepto ConcurrentSkipListSetdonde está O(n).
Ian Roberts
0

También puede obtener alguna utilidad de un mapa bidireccional como el BiMapde Google Guava

Con a BiMap, puede asignar de manera bastante eficiente un entero (para acceso de índice aleatorio) a cualquier otro tipo de objeto. BiMaps son uno a uno, por lo que cualquier número entero tiene, como máximo, un elemento asociado y cualquier elemento tiene un número entero asociado. Está inteligentemente respaldado por dos HashTableinstancias, por lo que usa casi el doble de memoria, pero es mucho más eficiente que una costumbre Listen cuanto al procesamiento porque contains()(que se llama cuando se agrega un elemento para verificar si ya existe) es un tiempo constante y operación amigable en paralelo como HashSet's, mientras que Listla implementación de' es MUCHO más lenta.

Steve K
fuente
0

Tuve un problema similar. No necesitaba un conjunto ordenado, sino más bien una lista con un indexOf/ contains. Como no encontré nada, implementé uno yo mismo. Aquí está el código, implementa ambos Sety List, aunque no todas las operaciones de lista masiva son tan rápidas como las ArrayListversiones.

descargo de responsabilidad: no probado

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;
import java.util.Collection;
import java.util.Comparator;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
import static java.util.Objects.requireNonNull;

/**
 * An ArrayList that keeps an index of its content so that contains()/indexOf() are fast. Duplicate entries are
 * ignored as most other java Set's do.
 */
public class IndexedArraySet<E> extends ArrayList<E> implements Set<E> {

    public IndexedArraySet() { super(); }

    public IndexedArraySet(Iterable<E> c) {
        super();
        addAll(c);
    }

    private HashMap<E, Integer> indexMap = new HashMap<>();

    private void reindex() {
        indexMap.clear();
        int idx = 0;
        for (E item: this) {
            addToIndex(item, idx++);
        }
    }

    private E addToIndex(E e, int idx) {
        indexMap.putIfAbsent(requireNonNull(e), idx);
        return e;
    }

    @Override
    public boolean add(E e) {
        if(indexMap.putIfAbsent(requireNonNull(e), size()) != null) return false;
        super.add(e);
        return true;
    }

    @Override
    public boolean addAll(Collection<? extends E> c) {
        return addAll((Iterable<? extends E>) c);
    }
    public boolean addAll(Iterable<? extends E> c) {
        boolean rv = false;
        for (E item: c) {
            rv |= add(item);
        }
        return rv;
    }

    @Override
    public boolean contains(Object e) {
        return indexMap.containsKey(e);
    }

    @Override

    public int indexOf(Object e) {
        if (e == null) return -1;
        Integer i = indexMap.get(e);
        return (i == null) ? -1 : i;
    }

    @Override
    public int lastIndexOf(Object e) {
        return indexOf(e);
    }

    @Override @SuppressWarnings("unchecked")
    public Object clone() {
        IndexedArraySet clone = (IndexedArraySet) super.clone();
        clone.indexMap = (HashMap) indexMap.clone();
        return clone;
    }

    @Override
    public void add(int idx, E e) {
        if(indexMap.putIfAbsent(requireNonNull(e), -1) != null) return;
        super.add(idx, e);
        reindex();
    }

    @Override
    public boolean remove(Object e) {
        boolean rv;
        try { rv = super.remove(e); }
        finally { reindex(); }
        return rv;
    }

    @Override
    public void clear() {
        super.clear();
        indexMap.clear();
    }

    @Override
    public boolean addAll(int idx, Collection<? extends E> c) {
        boolean rv;
        try {
            for(E item : c) {
                // check uniqueness
                addToIndex(item, -1);
            }
            rv = super.addAll(idx, c);
        } finally {
            reindex();
        }
        return rv;
    }

    @Override
    public boolean removeAll(Collection<?> c) {
        boolean rv;
        try { rv = super.removeAll(c); }
        finally { reindex(); }
        return rv;
    }

    @Override
    public boolean retainAll(Collection<?> c) {
        boolean rv;
        try { rv = super.retainAll(c); }
        finally { reindex(); }
        return rv;
    }

    @Override
    public boolean removeIf(Predicate<? super E> filter) {
        boolean rv;
        try { rv = super.removeIf(filter); }
        finally { reindex(); }
        return rv;
    }

    @Override
    public void replaceAll(final UnaryOperator<E> operator) {
        indexMap.clear();
        try {
            int duplicates = 0;
            for (int i = 0; i < size(); i++) {
                E newval = requireNonNull(operator.apply(this.get(i)));
                if(indexMap.putIfAbsent(newval, i-duplicates) == null) {
                    super.set(i-duplicates, newval);
                } else {
                    duplicates++;
                }
            }
            removeRange(size()-duplicates, size());
        } catch (Exception ex) {
            // If there's an exception the indexMap will be inconsistent
            reindex();
            throw ex;
        }

    }

    @Override
    public void sort(Comparator<? super E> c) {
        try { super.sort(c); }
        finally { reindex(); }
    }
}
JanKanis
fuente