Listen-Iteratoren garantieren in erster Linie, dass Sie die Elemente der Liste in der internen Reihenfolge der Liste erhalten (aka. Einfügeauftrag ). Genauer gesagt geht es um die Reihenfolge, in der Sie die Elemente eingefügt haben, oder darum, wie Sie die Liste manipuliert haben. Das Sortieren kann als eine Manipulation der Datenstruktur betrachtet werden, und es gibt mehrere Möglichkeiten, die Liste zu sortieren.
Ich ordne die Wege in der Reihenfolge von Nützlichkeit wie ich es persönlich sehe:
1. Erwägen Sie die Verwendung Set
o Bag
Sammlungen stattdessen
HINWEIS: Ich habe diese Option an den Anfang gestellt, weil Sie dies normalerweise ohnehin tun wollen.
Eine sortierte Menge sortiert die Sammlung automatisch beim Einfügen Das bedeutet, dass es die Sortierung vornimmt, während Sie der Sammlung Elemente hinzufügen. Das bedeutet auch, dass Sie sie nicht manuell sortieren müssen.
Wenn Sie sicher sind, dass Sie sich keine Sorgen über doppelte Elemente machen müssen (oder solche haben), können Sie außerdem die TreeSet<T>
stattdessen. Sie implementiert SortedSet
y NavigableSet
Schnittstellen und funktioniert so, wie man es von einer Liste erwarten würde:
TreeSet<String> set = new TreeSet<String>();
set.add("lol");
set.add("cat");
// automatically sorts natural order when adding
for (String s : set) {
System.out.println(s);
}
// Prints out "cat" and "lol"
Wenn Sie die natürliche Reihenfolge nicht wünschen, können Sie den Konstruktorparameter verwenden, der eine Comparator<T>
.
Alternativ können Sie auch Multisets (auch bekannt als Taschen ) das ist ein Set
die stattdessen doppelte Elemente zulässt, und es gibt Implementierungen von Drittanbietern dafür. Vor allem von der Guava-Bibliotheken gibt es eine TreeMultiset
die ähnlich funktioniert wie die TreeSet
.
2. Sortieren Sie Ihre Liste mit Collections.sort()
Wie bereits erwähnt, ist die Sortierung von List
s ist eine Manipulation der Datenstruktur. In Situationen, in denen Sie "eine einzige Quelle der Wahrheit" benötigen, die auf verschiedene Weise sortiert werden soll, ist die manuelle Sortierung der richtige Weg.
Sie können Ihre Liste mit der Funktion java.util.Collections.sort()
Methode. Hier ist ein Code-Beispiel dafür:
List<String> strings = new ArrayList<String>()
strings.add("lol");
strings.add("cat");
Collections.sort(strings);
for (String s : strings) {
System.out.println(s);
}
// Prints out "cat" and "lol"
Verwendung von Komparatoren
Ein klarer Vorteil ist, dass Sie Folgendes nutzen können Comparator
において sort
Methode. Java bietet auch einige Implementierungen für die Comparator
wie zum Beispiel die Collator
was für die Sortierung von Zeichenketten nach Gebietsschema nützlich ist. Hier ist ein Beispiel:
Collator usCollator = Collator.getInstance(Locale.US);
usCollator.setStrength(Collator.PRIMARY); // ignores casing
Collections.sort(strings, usCollator);
Sortierung in konkurrierenden Umgebungen
Beachten Sie jedoch, dass die Verwendung des sort
Methode ist in konkurrierenden Umgebungen nicht freundlich, da die Sammlungsinstanz manipuliert wird, und Sie sollten stattdessen unveränderliche Sammlungen verwenden. Dies ist etwas, das Guava in der Ordering
Klasse und ist ein einfacher Einzeiler:
List<string> sorted = Ordering.natural().sortedCopy(strings);
3. Umschließen Sie Ihre Liste mit java.util.PriorityQueue
Obwohl es in Java keine sortierte Liste gibt, gibt es doch eine sortierte Warteschlange, die wahrscheinlich genauso gut für Sie funktionieren würde. Es ist die java.util.PriorityQueue
Klasse.
Nico Haase hat in den Kommentaren auf eine zugehörige Frage die auch diese Frage beantwortet.
In einer sortierten Sammlung Sie wollen höchstwahrscheinlich nicht manipulieren die interne Datenstruktur, weshalb PriorityQueue die Schnittstelle List nicht implementiert (denn dann hätte man direkten Zugriff auf ihre Elemente).
Vorbehalt gegen die PriorityQueue
Iterator
Le site PriorityQueue
Klasse implementiert die Iterable<E>
y Collection<E>
Schnittstellen, so dass sie wie üblich iteriert werden kann. Es ist jedoch nicht garantiert, dass der Iterator Elemente in der sortierten Reihenfolge zurückgibt. Stattdessen (wie Alderath in den Kommentaren anmerkt) müssen Sie poll()
die Warteschlange, bis sie leer ist.
Beachten Sie, dass Sie eine Liste in eine Prioritätswarteschlange umwandeln können, indem Sie die Konstruktor, der eine beliebige Sammlung annimmt :
List<String> strings = new ArrayList<String>()
strings.add("lol");
strings.add("cat");
PriorityQueue<String> sortedStrings = new PriorityQueue(strings);
while(!sortedStrings.isEmpty()) {
System.out.println(sortedStrings.poll());
}
// Prints out "cat" and "lol"
4. Schreiben Sie Ihr eigenes SortedList
クラス
HINWEIS: Sie sollten das nicht tun müssen.
Sie können Ihre eigene Listenklasse schreiben, die jedes Mal sortiert, wenn Sie ein neues Element hinzufügen. Dies kann je nach Implementierung ziemlich rechenintensiv werden und ist sinnlos Es sei denn, Sie wollen es als Übung machen, und zwar aus zwei Gründen:
- Es bricht den Vertrag, der
List<E>
Schnittstelle hat, weil die add
Methoden sollten sicherstellen, dass sich das Element in dem vom Benutzer angegebenen Index befindet.
- Warum das Rad neu erfinden? Sie sollten stattdessen das TreeSet oder Multisets verwenden, wie im ersten Punkt oben beschrieben.
Wenn Sie es jedoch als Übung machen wollen, finden Sie hier ein Codebeispiel, das Ihnen den Einstieg erleichtern soll; es verwendet die AbstractList
abstrakte Klasse:
public class SortedList<E> extends AbstractList<E> {
private ArrayList<E> internalList = new ArrayList<E>();
// Note that add(E e) in AbstractList is calling this one
@Override
public void add(int position, E e) {
internalList.add(e);
Collections.sort(internalList, null);
}
@Override
public E get(int i) {
return internalList.get(i);
}
@Override
public int size() {
return internalList.size();
}
}
Beachten Sie, dass, wenn Sie die benötigten Methoden nicht überschrieben haben, die Standardimplementierungen von AbstractList
wirft UnsupportedOperationException
s.
3 Stimmen
Hat stackoverflow.com/questions/416266/sorted-collection-in-java Ihnen helfen?
9 Stimmen
Welches Ergebnis erwarten Sie also, wenn Sie ein Element in der Mitte der Liste einfügen?
6 Stimmen
@bestsss Es wäre durchaus möglich, eine SortedList-Klasse zu haben, die nicht die java.util.List-Schnittstelle implementiert. Lesen Sie die Frage als eine Anfrage, warum es keine Datenstruktur gibt, die die gewünschte Funktionalität unterstützt. Lassen Sie sich nicht von unbedeutenden Details wie der Namensgebung ablenken.
0 Stimmen
@Alderath, die übliche Struktur dafür ist ein Baum (rot/schwarz, avl oder btree) mit zusätzlichen prev/next-Links zur Unterstützung der Ordnung. Ich verwende eine ähnliche Struktur rot/schwarz mit vorhergehenden/nächsten Links. Das ist allerdings eine ziemliche Nischenanwendung. Der Baum kann sowohl in geordneter als auch in eingefügter Reihenfolge durchlaufen werden, hat O(logn) find/contains aber get(int) ist O(n). Angesichts der Tatsache, dass es sich um eine Nischenanwendung handelt, würde ich annehmen, dass es den Entwicklern überlassen wurde, eine solche zu implementieren, wenn sie es brauchen.
6 Stimmen
Nicht die Beantwortung der Frage nach dem "Warum", sondern die einfachste Abhilfe für TreeSet ist es, einen Comparator zu verwenden, der niemals Null zurückgibt, z. B.
int diff = this.score - that.score;
return (diff == 0) ? 1 : diff;
Da es sich um einen stinkenden Hack handelt, würde ich dies als anonymes Konstruktorargument bereitstellen, anstatt Comparable zu implementieren.0 Stimmen
@earcam Wie soll man in so einem Set überhaupt etwas finden?
set.add(X); Y = set.get(X);
wird mit Sicherheit null zurückgeben.0 Stimmen
@user13784117, ich glaube, Sie haben das falsch verstanden und BTW
Set
implementiert nichtget
- Notiz; ich würde sagen, es ist ein Stinkmorchel - Der Punkt, auf den ich hinauswollte, ist, dass mein Vorschlag mehrere identische Werte in einerSet
und tut dies amadd
Vermeidung von Kosten fürCollections.sort(List)
. Dieser stinkende Hack versagt bei einer Reihe von Szenarien, wie zum Beispielcontains
- nicht für die Verwendung, sondern für die Konvertierung gedacht war; ich vermute stark, dass es schneller ist (in Bezug auf die Zeitkomplexität für riesig Sammlungen) zu verwenden und dann in eine Liste mit z.B. zu dumpen.ArrayList
Konstrukteur oderaddAll
(unwahrscheinlich, dass es besser für Big-O-Speicher ist)0 Stimmen
Mein Fehler bei
get
- aber ähnliche Argumente fürcontains
. Außerdem verstößt Ihr Komparator sicherlich gegen einen Vertrag, da compare(a,b) == 1 und compare(b,a) == 1, d.h. a > b und b > a. Ich würde zumindest für Konsistenz im Fall von a.equals(b) durch die Bestellung auf etwas anderes, vielleicht Vergleichen von System.identityHashCode-Werte.0 Stimmen
Ich habe diesen interessanten Hack ausprobiert, aber er hat auch den Nachteil, dass set.removeAll(set2) nichts entfernt, weil die removeAll-Methode irgendwie den Comparator verwendet.