Irgendeine Implementierung von Ordered Set in Java?


98

Wenn jemand vertraut mit Objective-C ist , gibt es eine Sammlung genannt , NSOrderedSetdass fungiert als Set und seine Elemente können als zugegriffen werden Array ‚s diejenigen.

Gibt es so etwas in Java?

Ich habe gehört, dass es eine Sammlung namens gibt LinkedHashMap, aber ich habe so etwas für ein Set nicht gefunden.


Ich arbeite an einem ähnlichen Problem in C ++. Können wir mit NSOrderedSet auf Elemente in der Reihenfolge zugreifen, in der wir sie eingefügt haben?
Vinay

Wissen Sie, wie Sie die Funktionalität in C ++ übertreffen können? i, e fungiert als SET und kann als Element eines Arrays aufgerufen werden?
Vinay

Antworten:


117

Schauen Sie sich die LinkedHashSet- Klasse an

Aus dem Java-Dokument :

Implementierung der Hash-Tabelle und der verknüpften Liste der Set-Schnittstelle mit vorhersagbarer Iterationsreihenfolge . Diese Implementierung unterscheidet sich von HashSet dadurch, dass eine doppelt verknüpfte Liste geführt wird, die alle Einträge durchläuft. Diese verknüpfte Liste definiert die Iterationsreihenfolge, dh die Reihenfolge, in der Elemente in die Menge eingefügt wurden (Einfügereihenfolge) . Beachten Sie, dass die Einfügereihenfolge nicht beeinflusst wird, wenn ein Element erneut in die Menge eingefügt wird . (Ein Element e wird erneut in eine Menge s eingefügt, wenn s.add (e) aufgerufen wird, wenn s.contains (e) unmittelbar vor dem Aufruf true zurückgeben würde.)


Vielen Dank. Es scheint trivial zu sein, LinkedHashMapaber ich habe es irgendwie nicht gefunden.
Uko


9
Warum bekommt diese Antwort so viele positive Stimmen? Dies ist überhaupt keine Antwort auf die Frage. Es gibt keine Funktion LinkedHashSet, mit der Sie herausfinden können, in welchem ​​Index sich das Element ebenfalls befindet.
Searchengine27

31

Jeder Satz hat einen Iterator (). Der Iterator eines normalen HashSet ist ziemlich zufällig, ein TreeSet führt ihn nach Sortierreihenfolge aus, ein LinkedHashSet- Iterator iteriert nach Einfügereihenfolge .

Sie können jedoch kein Element in einem LinkedHashSet ersetzen. Sie können eines entfernen und ein anderes hinzufügen, aber das neue Element ersetzt nicht das Original. In einer LinkedHashMap können Sie einen Wert für einen vorhandenen Schlüssel ersetzen. Die Werte befinden sich dann weiterhin in der ursprünglichen Reihenfolge.

Sie können auch nicht an einer bestimmten Position einfügen.

Vielleicht sollten Sie eine ArrayList mit einer expliziten Prüfung verwenden, um das Einfügen von Duplikaten zu vermeiden.


Ich möchte in der Lage sein, Elemente an einer bestimmten Position zu setzen / abzurufen und sie in der Reihenfolge zu erhalten, in der ich sie hinzugefügt habe. Es scheint, dass LinkedHashSetdas tun sollte. Vielen Dank für die Antwort
Uko

11

Schauen Sie sich das Java-Standard-API-Dokument an . Gleich daneben LinkedHashMapgibt es eine LinkedHashSet. Beachten Sie jedoch, dass die Reihenfolge in diesen die Einfügereihenfolge ist, nicht die natürliche Reihenfolge der Elemente. Und Sie können nur in dieser Reihenfolge iterieren, keinen Direktzugriff ausführen (außer durch Zählen der Iterationsschritte).

Es gibt auch eine Schnittstelle, SortedSetdie von TreeSetund implementiert wird ConcurrentSkipListSet. Beide erlauben die Iteration in der natürlichen Reihenfolge ihrer Elemente oder in einer Comparator, aber nicht zufälligen Zugriffs- oder Einfügereihenfolge.

Für eine Datenstruktur, die sowohl über einen effizienten Zugriff per Index verfügt als auch das festgelegte Kriterium effizient implementieren kann, benötigen Sie eine Überspringliste , aber es gibt keine Implementierung mit dieser Funktionalität in der Java Standard-API, obwohl ich sicher bin, dass es einfach ist, eine zu finden im Internet.


Ich kann Ihren Kommentar falsch verstehen, aber ich hatte den Eindruck, dass es seit Java 1.6 mehrere Standardsammlungen gab, die auf Überspringlisten basierten (wie z. B. ConcurrentSkipListSet usw.).
TacticalCoder

@ user988052: Ja, aber diese implementieren keinen Direktzugriff per Index (obwohl mein Verständnis von Sprunglisten besagt, dass dies möglich sein sollte), was Uko anscheinend will.
Michael Borgwardt

@MichaelBorgwardt Java 6 und höher enthält zwei Skip List-Implementierungen: ConcurrentSkipListMapund ConcurrentSkipListSet. Beide pflegen eine Sortierung basierend auf der natürlichen Reihenfolge oder einem Komparator. Ich verstehe nicht, ob sie den von Ihnen besprochenen Direktzugriff oder die Reihenfolge der Eingabe bieten.
Basil Bourque

@BasilBourque: guter Fund und danke für die Änderungen. OP wollte Zugriff per Index, und jetzt, wo ich es mir angesehen und darüber nachgedacht habe, denke ich, dass Überspringlisten diese Fähigkeit auch nicht haben ...
Michael Borgwardt

5

Dies ist die richtige Antwort. Im Gegensatz zu LHSet, TreeSet hat java.util.SortedSet implementieren.
vemv

40
bestellt und sortiert ist verschiedene Dinge. TreeSet ist sortiert, nicht bestellt
andrii

2
Genau, geordnet bezieht sich auf die Einfügereihenfolge (die Art und Weise, wie eine Liste funktioniert), während sortiert sich auf die nachträgliche Reihenfolge der Elemente basierend auf einigen Kriterien bezieht.
Cornel Masson

5

Versuchen Sie es mit java.util.TreeSetdiesen Geräten SortedSet.

So zitieren Sie das Dokument:

"Die Elemente werden in ihrer natürlichen Reihenfolge oder von einem zum festgelegten Erstellungszeitpunkt bereitgestellten Komparator sortiert, je nachdem, welcher Konstruktor verwendet wird."

Beachten Sie, dass Hinzufügen, Entfernen und Enthalten ein Zeitkostenprotokoll (n) enthält.

Wenn Sie als Array auf den Inhalt des Sets zugreifen möchten, können Sie ihn folgendermaßen konvertieren:

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

Dieses Array wird nach denselben Kriterien wie das TreeSet sortiert (natürlich oder von einem Komparator), und in vielen Fällen hat dies einen Vorteil, anstatt ein Arrays.sort () auszuführen.


1
Ich brauche wie in Arraylist ei Bestellung , wenn ich das erste Element gesetzt cund dann Element a, wie ich Iterierte über eine Sammlung , die ich ihnen in der gleichen Reihenfolge zu bekommen: c, ausw.
Uko

1

Baumsatz ist ein geordneter Satz, aber Sie können nicht über einen Elementindex zugreifen, sondern nur durchlaufen oder zum Anfang / Ende gehen.


Mit treeSet entstehen höhere Kosten. LinkedHashSet hat geringere Kosten.
Carlos

0

Wenn es sich um eine kostengünstige Implementierung der Überspringliste handelt, frage ich mich in Bezug auf Big O, wie hoch die Kosten für diese Operation sind:

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

Ich meine, es bleibt immer in einer ganzen Array-Kreation stecken, also ist es O (n):

java.util.Arrays#copyOf

1
Dies hängt von den Leistungsmerkmalen des Iterators und der size()Methode des zugrunde liegenden Satzes ab. Iteration ist normalerweise O(n), Größe ist normalerweise O(1)außer ConcurrentSkipListSetwo es ist O(n).
Ian Roberts


0

Möglicherweise erhalten Sie auch ein Dienstprogramm aus einer bidirektionalen Karte wie der BiMapvon Google Guava

Mit a BiMapkönnen Sie eine Ganzzahl (für den zufälligen Indexzugriff) ziemlich effizient einem anderen Objekttyp zuordnen. BiMaps sind eins zu eins, so dass jeder gegebenen Ganzzahl höchstens ein Element zugeordnet ist und jedem Element eine ganze Zahl zugeordnet ist. Es wird geschickt von zwei HashTableInstanzen untermauert , verwendet also fast das Doppelte des Speichers, ist jedoch in Bezug auf die ListVerarbeitung viel effizienter als ein benutzerdefinierter Speicher, da contains()(der aufgerufen wird, wenn ein Element hinzugefügt wird, um zu überprüfen, ob es bereits vorhanden ist) eine konstante Zeit ist und parallel-freundlicher Betrieb wie HashSet's, während Listdie Implementierung viel langsamer ist.


0

Ich hatte ein ähnliches Problem. Ich brauchte kein bestelltes Set, sondern eine Liste mit einem schnellen indexOf/ contains. Da ich dort nichts herausgefunden habe, habe ich selbst eines implementiert. Hier ist der Code, der beide implementiert Setund Listobwohl nicht alle Massenlistenoperationen so schnell sind wie die ArrayListVersionen.

Haftungsausschluss: nicht getestet

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(); }
    }
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.