Der Artikel ist eine Übersetzung des Artikels
„ Top 10 Fragen zu Java Collections “ . Nachfolgend finden Sie die häufigsten Fragen zu Sammlungen in Java, die auf Stackowerflow gestellt und diskutiert werden. Bevor Sie sich mit diesen Fragen befassen, wäre es gut, einen Blick auf das Klassenhierarchiediagramm zu werfen.
1. Wann sollte LinkedList anstelle von ArrayList verwendet werden?
Eine ArrayList ist eigentlich ein Array; auf seine Elemente kann direkt über den Index zugegriffen werden. Sollte das Array überlaufen, ist ein neues mit mehr Platz notwendig. Das Platzieren und Verschieben aller Elemente dauert O(n) Zeit. Außerdem ist das Hinzufügen und Entfernen von Elementen erforderlich, um vorhandene Elemente im Array zu verschieben. Dies ist vielleicht der größte Nachteil bei der Verwendung von ArrayList. LinkedList ist eine doppelte Liste von Elementlinks. Um auf das Element in der Mitte zuzugreifen, müssen Sie also vom Anfang bis zum Ende des Blattes suchen. Andererseits ist das Hinzufügen und Entfernen eines Elements in einer LinkedList schneller, da diese Vorgänge nur die Liste selbst ändern. Im Folgenden werden die schlimmsten Zeiten verglichen:
Methode |
Anordnungsliste |
LinkedList |
get(index) |
O(1) |
An) |
hinzufügen(E) |
An) |
O(1) |
add(E, Index) |
An) |
An) |
entfernen(Index) |
An) |
An) |
Iterator.remove() |
An) |
O(1) |
Iterator.add(E) |
An) |
O(1) |
Trotz der Laufzeit muss bei großen Listen die Speichernutzung individuell betrachtet werden. In einer LinkedList muss jeder Knoten mindestens zwei zusätzliche Zeiger haben, um den vorherigen und den nächsten Knoten zu verknüpfen, während in einer ArrayList nur ein Array von Elementen erforderlich ist. Weitere Vergleiche von ArrayList-, LinkedList- und Vector-
Listen .
2. Effizientes Äquivalent zum Entfernen von Elementen während der Sammlungsiteration. Die einzig richtige Möglichkeit, eine Sammlung während der Iteration zu ändern (Elemente zu entfernen), ist die Verwendung von
Iterator.remove() . Beispiel: Der häufigste Fehler ist: Beim Ausführen des obigen Codes wird
eine ConcurrentModificationException angezeigt . Dies geschieht, weil der Iterator so generiert wurde, dass er sich durch die gesamte Liste bewegt, aber gleichzeitig das Blatt durch den Aufruf von Iterator.remove() geändert wird. Wie in der Dokumentation zu dieser Ausnahme beschrieben,
Iterator
itr = list.iterator(); while(itr.hasNext()) { // do something itr.remove(); }
for(Integer i: list) { list.remove(i); }
„Es ist im Allgemeinen nicht zulässig, dass ein Thread eine Sammlung ändert, während ein anderer Thread darüber iteriert.“
Im Allgemeinen ist es nicht akzeptabel, dass ein Thread eine Sammlung ändert, während ein anderer Thread sie durchläuft.
3. Wie konvertiere ich eine Liste in ein int[]-Array? Der einfachste Weg, dies zu tun, ist die Verwendung
von ArrayUtils , das sich in der
Apache Commons Lang- Bibliothek befindet .
int[] array = ArrayUtils.toPrimitive(list.toArray(new Integer[0]));
Für diesen Ausdruck gibt es im JDK keine Verknüpfung. Denken Sie daran, dass Sie List.toArray() nicht verwenden können, da dieser Ausdruck List in Integer[] konvertiert (was
kein primitiver Typ ist). Der richtige Weg wäre die folgende Option:
int[] array = new int[list.size()]; for(int i=0; i < list.size(); i++) { array[i] = list.get(i); }
4. Wie konvertiere ich ein int[]-Array in eine Liste? Der einfachste Weg ist auch die Verwendung von
ArrayUtils in der
Apache Commons Lang- Bibliothek , wie oben beschrieben.
List list = Arrays.asList(ArrayUtils.toObject(array));
Außerdem gibt es im JDK keine Verknüpfung für diesen Ausdruck.
5. Wie kann ich die Sammlung am besten filtern? Sie können Pakete von Drittanbietern wie
Guava oder
Apache Commons Lang verwenden , um die Funktionalität zu erhöhen. Beide Pakete verfügen über eine filter()-Methode (in der
Collections2- Klasse von Guava und
CollectionUtils von Apache). Die Methode filter() gibt Elemente zurück, die mit dem angegebenen Prädikat übereinstimmen. Im JDK ist alles komplizierter. Die gute Nachricht ist, dass in Java 8 Prädikate hinzugefügt werden
, aber jetzt müssen Sie Iterator verwenden, um die gesamte Sammlung zu durchlaufen. Natürlich können Sie den von Guava und Apache eingeschlagenen Weg nachahmen, indem Sie sich mit der neuen Predicate-Schnittstelle vertraut machen. Jetzt können wir den folgenden Code verwenden, um die Sammlung zu filtern:
6. Wie konvertiert man List einfach in Set? Dafür gibt es zwei Möglichkeiten, je nachdem, wie Sie Gleichheit definieren möchten. Der erste Codeteil fügt die Liste in ein HashSet ein. Das Duplikat wird in diesem Fall hauptsächlich durch hashCode() bestimmt. Normalerweise funktioniert das. Wenn Sie jedoch den Vergleichspfad berücksichtigen müssen, ist es besser, den zweiten Teil des Codes zu verwenden, in dem Sie Ihren eigenen Komparator definieren können.
7. Wie kann ich doppelte Elemente aus einer ArrayList entfernen? Diese Frage hängt in gewisser Weise mit der obigen Frage zusammen. Wenn Ihnen die Reihenfolge der Elemente in der ArrayList egal ist, wäre es ein kluger Schachzug, das Blatt in einem Set zu platzieren, um Duplikate zu entfernen, und es dann wieder in die Liste zurückzugeben. Unten finden Sie ein Beispiel. Wenn Ihnen die Reihenfolge der Elemente wichtig ist, können Sie die Reihenfolge sicherstellen, indem Sie die Liste in
einem LinkedHashSet platzieren , das im Standard-JDK enthalten ist.
8. Sortierte Sammlung
int[] array = {1,2,3,4,5}; List
list = new ArrayList
(); for(int i: array) { list.add(i); }
Iterator
itr = list.iterator(); while(itr.hasNext()) { int i = itr.next(); if (i > 5) { // filter all ints bigger than 5 itr.remove(); } }
public interface Predicate
{ boolean test(T o); } public static
void filter(Collection
collection, Predicate
predicate) { if ((collection != null) && (predicate != null)) { Iterator
itr = collection.iterator(); while(itr.hasNext()) { T obj = itr.next(); if (!predicate.test(obj)) { itr.remove(); } } } }
filter(list, new Predicate
() { public boolean test(Integer i) { return i <= 5; } });
Set
set = new HashSet
(list);
Set
set = new TreeSet
(aComparator); set.addAll(list);
ArrayList** list = ... // initial a list with duplicate elements Set
set = new HashSet
(list); list.clear(); list.addAll(set);
Es gibt mehrere Möglichkeiten, eine sortierte Sammlung in Java zu unterstützen. Alle bieten eine Sammlung in natürlicher Reihenfolge oder nach einem bestimmten Komparator. Bei natürlicher Reihenfolge müssen Sie auch die
Comparable- Schnittstelle für das Element implementieren.
- Collections.sort() kann eine Liste sortieren. Wie in der Java-Dokumentation angegeben, ist diese Sortierung stabil und garantiert eine Leistung von n log(n).
- PriorityQueue sorgt für eine geordnete Warteschlange. Der Unterschied zwischen PriorityQueue und Collections.sort() besteht darin, dass PriorityQueue die Reihenfolge der Warteschlange ständig beibehält, Sie jedoch nur das erste Element der Warteschlange abrufen können. Sie können nicht zufällig auf ein Element wie PriorityQueue.get(4) zugreifen.
- Wenn in der Sammlung keine Duplikate vorhanden sind, können Sie TreeSet auswählen . Ebenso wie PriorityQueue verwaltet TreeSet jederzeit eine geordnete Menge. Sie können das kleinste oder größte Element aus einem TreeSet abrufen, haben aber trotzdem keinen wahlfreien Zugriff auf die Elemente.
Einfach ausgedrückt stellt Collections.sort() eine einmalig geordnete Liste bereit. PriorityQueue und TreeSet verwalten jederzeit eine geordnete Sammlung, allerdings auf Kosten des fehlenden indizierten Zugriffs auf Elemente.
9. Collections.emptyList() oder neue Instanz Die gleiche Frage gilt für emptyMap() und emptySet(). Beide Methoden geben eine leere Liste zurück, Collections.emptyList() ist jedoch eine unveränderliche Liste. Das bedeutet, dass Sie einer „leeren“ Liste keine neuen Elemente hinzufügen
können . Im Hintergrund erstellt jeder Aufruf der Collections.emptyList()-Methode nicht tatsächlich eine neue Instanz der leeren Liste. Stattdessen wird die bereits vorhandene leere Instanz wiederverwendet.
Wenn Sie Singleton als Entwurfsmuster kennen , sollten Sie verstehen, was gemeint ist. Dies sollte bei häufigen Anrufen zu
einer besseren Leistung führen .
10 Eine Sammlung kopieren, Collections.copy() Es gibt zwei Möglichkeiten, eine Quellliste in eine Zielliste zu kopieren. Eine Möglichkeit besteht darin, den ArrayList-Konstruktor zu verwenden. Eine andere Möglichkeit ist die Verwendung der
Collections.copy()- Methode . Beachten Sie die erste Zeile: Wir weisen eine Liste zu, die mindestens so lang ist wie die ursprüngliche Liste, da in der Java-Dokumentation zu Sammlungen Folgendes steht:
ArrayList
dstList = new ArrayList
(srcList);
Die Zielliste muss mindestens so lang sein wie die Quellliste.
Das bedeutet, dass die endgültige Liste nicht kürzer sein darf als die ursprüngliche. Bei beiden Methoden handelt es sich um flaches Kopieren. Was ist also der Unterschied zwischen diesen beiden Methoden? Erstens weist Collections.copy() die Kapazität der dstList-Sammlung nicht neu zu, selbst wenn die dstList nicht über genügend Platz verfügt, um alle Elemente aus der srcList aufzunehmen. Stattdessen wird
eine IndexOutOfBoundsException ausgelöst . Man könnte sich fragen, ob das irgendeinen Vorteil hat. Der Grund dafür ist, dass dadurch sichergestellt wird, dass die Methode zeitlich linear abläuft. Dies eignet sich auch, wenn Sie Arrays wiederverwenden möchten, anstatt Speicher im ArrayList-Konstruktor neu zuzuweisen.
Anstelle eines Fazits Wenn Sie nach der Lektüre des Artikels noch Fragen haben, können Sie diese gerne in den Kommentaren stellen. Wenn Sie außerdem eine Ungenauigkeit in der Übersetzung oder einen anderen Fehler feststellen, schreiben Sie an die PM, sie wird korrigiert und es wird Ihnen gedankt.
Original.
ArrayList
dstList = new ArrayList
(srcList.size()); Collections.copy(dstList, srcList);
GO TO FULL VERSION