Qualche implementazione di Ordered Set in Java?


98

Se qualcuno ha familiarità con Objective-C, esiste una raccolta chiamata NSOrderedSetche funge da Set e ai suoi elementi è possibile accedere come Array .

C'è qualcosa di simile in Java?

Ho sentito che c'è una collezione chiamata LinkedHashMap, ma non ho trovato niente di simile per un set.


Sto lavorando a un problema simile in c ++. con NSOrderedSet, possiamo accedere agli elementi nell'ordine in cui li abbiamo inseriti?
Vinay

Sai come ottenere le funzionalità di cui sopra in C ++? io, e agendo come SET e vi si può accedere come elementi di un array?
Vinay

Risposte:


118

Dai un'occhiata alla classe LinkedHashSet

Dal documento Java :

Implementazione di tabelle hash ed elenchi collegati dell'interfaccia Set, con ordine di iterazione prevedibile . Questa implementazione differisce da HashSet in quanto mantiene un elenco a doppio collegamento che attraversa tutte le sue voci. Questo elenco collegato definisce l'ordine di iterazione, che è l'ordine in cui gli elementi sono stati inseriti nel set (ordine di inserimento) . Tieni presente che l'ordine di inserimento non viene influenzato se un elemento viene reinserito nel set . (Un elemento e viene reinserito in un insieme s se s.add (e) viene invocato quando s.contains (e) restituisce true immediatamente prima dell'invocazione.).


Grazie mille. Sembra banale da guardare LinkedHashMapma non l'ho trovato in qualche modo.
Uko


9
Perché questa risposta sta ottenendo così tanti voti positivi? Questa non è affatto una risposta alla domanda. Non esiste alcuna funzione LinkedHashSetche ti permetta di capire in quale indice si trova anche l'elemento.
searchengine27

31

Ogni set ha un iteratore (). Un normale iteratore di HashSet è abbastanza casuale, un TreeSet lo fa in base all'ordinamento, un iteratore LinkedHashSet esegue l' iterazione in base all'ordine di inserimento.

Tuttavia, non puoi sostituire un elemento in un LinkedHashSet. Puoi rimuoverne uno e aggiungerne un altro, ma il nuovo elemento non sarà al posto dell'originale. In una LinkedHashMap, puoi sostituire un valore per una chiave esistente, quindi i valori saranno ancora nell'ordine originale.

Inoltre, non puoi inserire in una certa posizione.

Forse è meglio usare un ArrayList con un controllo esplicito per evitare di inserire duplicati.


Voglio essere in grado di impostare / ottenere elementi su una posizione specifica e di ottenerli per ordine li ho aggiunti. Sembra che LinkedHashSetdovrebbe farlo. Grazie per la risposta
Uko

11

Dai un'occhiata al documento API standard di Java . Proprio accanto a LinkedHashMap, c'è un file LinkedHashSet. Ma nota che l'ordine in questi è l'ordine di inserzione, non l'ordine naturale degli elementi. E puoi solo iterare in quell'ordine, non eseguire l'accesso casuale (eccetto contando i passaggi di iterazione).

C'è anche un'interfaccia SortedSetimplementata da TreeSete ConcurrentSkipListSet. Entrambi consentono l'iterazione nell'ordine naturale dei loro elementi o un Comparator, ma non un accesso casuale o un ordine di inserimento.

Per una struttura dati che ha sia un accesso efficiente per indice che può implementare in modo efficiente il criterio impostato, avresti bisogno di un elenco di salto , ma non c'è implementazione con quella funzionalità nell'API Java Standard, anche se sono certo che sia facile trovarne uno su internet.


Potrei aver frainteso il tuo commento, ma avevo l'impressione che da Java 1.6 ci fossero diverse raccolte predefinite basate su elenchi di salto (come, ad esempio, ConcurrentSkipListSet ecc.).
TacticalCoder

@ user988052: sì, ma quelli non implementano l'accesso casuale per indice (anche se la mia comprensione degli elenchi da saltare dice che dovrebbe essere possibile), che sembra essere ciò che vuole Uko.
Michael Borgwardt

@MichaelBorgwardt Java 6 e versioni successive include un paio di implementazioni di Skip List: ConcurrentSkipListMape ConcurrentSkipListSet. Entrambi mantengono un ordinamento basato sull'ordine naturale o un Comparatore. Non capisco se forniscono l'accesso casuale o l'ordine di ingresso di cui parli.
Basil Bourque

@BasilBourque: buona scoperta e grazie per le modifiche. OP voleva l'accesso tramite indice, e ora che l'ho guardato e ci penso, penso che le liste di salto in realtà non abbiano nemmeno quella capacità ...
Michael Borgwardt

5

Questa è la risposta corretta. A differenza LHSet, TreeSet non implementa java.util.SortedSet.
vemv

40
ordinato e ordinato è cose diverse. TreeSet è ordinato, non ordinato
andrii

2
Esattamente, ordinato si riferisce all'ordine di inserzione (il modo in cui funziona una Lista), mentre ordinato si riferisce all'ordinamento post-fatto degli elementi in base ad alcuni criteri.
Cornel Masson

5

Prova a usare java.util.TreeSetquell'attrezzo SortedSet.

Per citare il documento:

"Gli elementi vengono ordinati utilizzando il loro ordinamento naturale o da un comparatore fornito al momento della creazione dell'insieme, a seconda del costruttore utilizzato"

Notare che aggiungi, rimuovi e contiene ha un registro del costo del tempo (n).

Se vuoi accedere al contenuto del set come Array, puoi convertirlo facendo:

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

Questo array verrà ordinato con gli stessi criteri del TreeSet (naturale o da un comparatore), e in molti casi questo avrà un vantaggio invece di fare un Arrays.sort ()


1
Ho bisogno di ordinare come in ArrayList EI se metto primo elemento ce poi elemento a, come ho iterare su una collezione che voglio farli nello stesso ordine: c, aecc
Uko

1

Il set di alberi è un insieme ordinato, ma non è possibile accedere tramite un indice degli elementi, basta scorrere o andare all'inizio / alla fine.


Con treeSet dovrai sostenere un aumento dei costi. LinkedHashSet ha un costo inferiore.
Carlos

0

Se stiamo parlando di un'implementazione poco costosa della lista di salto, mi chiedo in termini di grande O, quale sia il costo di questa operazione:

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

Voglio dire, rimane sempre bloccato nella creazione di un intero array, quindi è O (n):

java.util.Arrays#copyOf

1
Ciò dipende dalle caratteristiche di prestazione dell'iteratore e dal size()metodo dell'insieme sottostante. L'iterazione è di solito O(n), la dimensione è solitamente O(1)tranne ConcurrentSkipListSetdove si trova O(n).
Ian Roberts


0

Potresti anche ottenere qualche utilità da una mappa bidirezionale come quella BiMapdi Google Guava

Con a BiMap, puoi mappare in modo abbastanza efficiente un intero (per l'accesso all'indice casuale) a qualsiasi altro tipo di oggetto. BiMaps sono uno a uno, quindi ogni intero dato ha, al massimo, un elemento ad esso associato, e ogni elemento ha un intero associato. È abilmente supportato da due HashTableistanze, quindi utilizza quasi il doppio della memoria, ma è molto più efficiente di un custom per Listquanto riguarda l'elaborazione perché contains()(che viene chiamato quando un elemento viene aggiunto per verificare se esiste già) è un tempo costante e operazioni parallele come quelle HashSetdi, mentre Listl'implementazione di è MOLTO più lenta.


0

Ho avuto un problema simile. Non avevo bisogno di un set ordinato ma più di un elenco con un veloce indexOf/ contains. Poiché non ho trovato nulla là fuori, ne ho implementato uno io stesso. Ecco il codice, implementa entrambi Sete List, sebbene non tutte le operazioni di elenco in blocco siano veloci come le ArrayListversioni.

disclaimer: non testato

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