Gibt es eine gleichzeitige Liste in Javas JDK?

277

Wie kann ich eine gleichzeitige Listeninstanz erstellen, in der ich über den Index auf Elemente zugreifen kann? Verfügt das JDK über Klassen oder Factory-Methoden, die ich verwenden kann?

AlikElzin-Kilaka
quelle
28
Warum nicht konstruktiv? Mehrere vorgeschlagene CopyOnWriteArrayList, die nicht in .Net gefunden werden. Man kann sagen, dass beide Fragen sich aufeinander beziehen, aber diese nicht zu schließen !!!
AlikElzin-Kilaka
1
Ich habe keine Ahnung, warum Jarrod Roberson es für eine gute Idee halten würde, die detaillierten Änderungen von Stephan zu übernehmen und sie auf die ursprüngliche, schlecht formulierte Frage zurückzuführen. Jarrods Antwort ist immer noch völlig akzeptabel. Tatsächlich ist CopyOnWriteArrayList die einzige gleichzeitige Klasse, die List im JDK implementiert. Verwirrt ...
Matt Passell
10
Da die akzeptierte Antwort auf die ursprüngliche Frage war und Stephan eine völlig unabhängige Frage mit einer Reihe von Quellcodes stellte, die das ursprüngliche Poster nicht enthielt , wurde die Frage nirgends vollständig geändert , wodurch mehr Antworten generiert wurden, die andere Dinge als Listdie des Originals vorschlugen sagt ist eine Anforderung, die als Vandalismus gilt. Ein Moderator hat die Frage bereits gesperrt, weil sich die Leute beschweren, dass die Antworten diese zerstörte Version der Frage nicht beantworten.
1
/ locked/ closed/ Vorheriger Kommentar
8
Es gibt keinen Grund, diese Frage zu schließen. Es wird nach Klassen im JDK gefragt, was nichts mit der Suche nach einer Bibliothek zu tun hat. Es ist die Basis von Java.
Maaartinus

Antworten:

175

In java.util.concurrent gibt es eine gleichzeitige Listenimplementierung . Insbesondere CopyOnWriteArrayList .

Basil Bourque
quelle
84
Beachten Sie, dass die gesamte Liste auf jeder Beilage kopiert wird, sodass sie häufig ineffizient ist.
dfrankow
22
@dfrankow Aber es kann mehr mehr effizient , wenn Sie Iterieren viel mehr als du bist Aktualisierung.
b1nary.atr0phy
Funktioniert nicht gut wie hier gezeigt. Ich habe Ausnahmen, obwohl ich nur die addAll-Methode verwende und sie mit stream lese. stackoverflow.com/questions/1527519/…
devssh
166

Wenn Sie keinen indexbasierten Zugriff benötigen und nur die Eigenschaften einer Liste beibehalten möchten, die die Einfügungsreihenfolge beibehalten, können Sie eine java.util.concurrent.ConcurrentLinkedQueue in Betracht ziehen . Da Iterable implementiert ist, können Sie nach dem Hinzufügen aller Elemente den Inhalt mithilfe der erweiterten Syntax durchlaufen:

Queue<String> globalQueue = new ConcurrentLinkedQueue<String>();

//Multiple threads can safely call globalQueue.add()...

for (String href : globalQueue) {
    //do something with href
}
Matt Passell
quelle
4
Ich denke, dass die vereinfachte for-Anweisung ( :) foreach heißt: docs.oracle.com/javase/1.5.0/docs/guide/language/foreach.html
AlikElzin-kilaka
2
@ AlikElzin-kilaka Du hast recht. Ich denke, dieser Name hat mich immer gestört, weil die eigentliche Syntax nicht das Wort "each" enthält, aber ich werde die Antwort aktualisieren, um den offiziellen Namen zu verwenden. :)
Matt Passell
3
@ AlikElzin-kilaka Nitpicking, aber laut JLS Version 8 wird es als "Enhanced for Statement" bezeichnet. Das gleiche gilt für das Java-Tutorial .
Roland
1
@ Roland definitiv nicht nitpicking. Es gibt (jetzt) ​​einen Unterschied zwischen "für jeden" und "erweitert für" in Java.
Hfontanez
2
@ Roland indirekt. Ich glaube, sie haben "für jede Schleife" in "erweitert für" umbenannt, um Verwechslungen zwischen Stream.forEach und dem, was jetzt als erweitert für bezeichnet wird, zu beseitigen.
Hfontanez
128

Sie können Collections.synchronizedList (List) sehr gut verwenden, wenn Sie lediglich eine einfache Aufrufsynchronisierung benötigen:

 List<Object> objList = Collections.synchronizedList(new ArrayList<Object>());
Yanick Rochon
quelle
70
Das Ergebnis von synchronizedListist "synchronisiert", aber nicht "gleichzeitig". Ein grundlegendes Problem ist, dass viele Listenoperationen - die indexbasiert sind - selbst nicht atomar sind und Teil eines größeren Konstrukts des gegenseitigen Ausschlusses sein müssen.
6
IMO, asing a Vectorist eher unkompliziert als Collections.synchronizedList(new ArrayList<Object>()).
Stephan
8
Das Ergebnis von synchronizedList ist "synchronisiert", aber nicht "gleichzeitig".
Kanagavelu Sugumar
46

Da das Erfassen der Position und das Abrufen des Elements von der angegebenen Position natürlich eine gewisse Sperrung erfordert (die Liste kann keine strukturellen Änderungen zwischen diesen beiden Operationen aufweisen).

Die Idee einer gleichzeitigen Sammlung besteht darin, dass jede Operation für sich atomar ist und ohne explizite Sperrung / Synchronisation ausgeführt werden kann.

Daher ist es in einer Situation, in der ein gleichzeitiger Zugriff erwartet wird, nicht sehr sinnvoll , das Element nvon einer bestimmten Listals atomare Operation an Position zu bringen.

Joachim Sauer
quelle
6
Joachim, ich glaube du hast den Nagel auf den Kopf getroffen. Nehmen Sie zum Beispiel eine schreibgeschützte Liste als gleichzeitige Liste. Es ist nicht nur sinnvoll, das Element an Position N aus der Liste zu entfernen, sondern es ist auch die Nussschale des Problems. Eine unveränderliche Liste (Kleinbuchstabe L) wäre also ein gutes Beispiel, aber keine Liste (Großbuchstabe L). Die CopyOnWriteArrayList ist gleichzeitig, aber viele Leute mögen die Leistung nicht. Eine Lösung nach dem Vorbild von Seilen (Schnurseilen) wäre wahrscheinlich ein guter Gewinner.
Johnstosh
1
Sehr guter Punkt. Die Liste, die das OP verwenden wird, kann jedoch eine sehr spezifische Verwendung haben. Zum Beispiel könnte es in einer gleichzeitigen Umgebung ausgefüllt, dann "gesperrt" (was auch immer es bedeutet) und dann sicher per Index zugegriffen werden. In der ersten Phase des Ausfüllens einer solchen Liste ist daher noch eine thread-sichere Implementierung erforderlich. Leider war das OP nicht genau festgelegt, wie die von ihm gesuchte Liste verwendet werden soll.
igor.zh
13

Sie haben folgende Möglichkeiten:

  • Collections.synchronizedList(): Sie jede wickeln können ListImplementierung ( ArrayList, LinkedListoder eine 3rd-Party - Liste). Der Zugriff auf jede Methode (Lesen und Schreiben) wird mit geschützt synchronized. Wenn Sie die iterator()for-Schleife verwenden oder erweitern, müssen Sie sie manuell synchronisieren. Während der Iteration werden andere Threads selbst beim Lesen vollständig blockiert. Sie können auch separat für jede synchronisieren hasNextund nextAnrufe, aber dann ConcurrentModificationExceptionsind möglich.

  • CopyOnWriteArrayList: Es ist teuer zu ändern, aber ohne Wartezeit zu lesen. Iteratoren werfen nie ConcurrentModificationException, sie geben zum Zeitpunkt der Iteratorerstellung einen Schnappschuss der Liste zurück, selbst wenn die Liste während der Iteration von einem anderen Thread geändert wird. Nützlich für selten aktualisierte Listen. Massenoperationen wie addAllwerden für Updates bevorzugt - das interne Array wird weniger oft kopiert.

  • Vector: sehr ähnlich synchronizedList, aber die Iteration ist auch synchronisiert. Iteratoren können jedoch auslösen ConcurrentModificationException, wenn der Vektor während der Iteration von einem anderen Thread geändert wird.

Andere Optionen:

  • Collections.unmodifiableList(): sperrenfrei, threadsicher, aber nicht veränderbar
  • Queueoder Dequekönnte eine Alternative sein, wenn Sie nur am Ende der Liste hinzufügen / entfernen und die Liste iterieren. Es gibt keinen Zugriff per Index und kein Hinzufügen / Entfernen an beliebigen Stellen. Sie haben mehrere gleichzeitige Implementierungen mit besserer Leistung und besserem gleichzeitigen Zugriff, aber dies geht über den Rahmen dieser Frage hinaus. Sie können sich auch JCTools ansehen , die leistungsfähigere Warteschlangenimplementierungen enthalten, die auf einzelne Verbraucher oder einzelne Hersteller spezialisiert sind.
Oliv
quelle
4

Geben Sie hier die Bildbeschreibung ein

CopyOnWriteArrayList ist eine thread-sichere Variante von ArrayList, bei der alle mutativen Operationen (Hinzufügen, Festlegen usw.) implementiert werden, indem eine neue Kopie des zugrunde liegenden Arrays erstellt wird.

CopyOnWriteArrayList ist eine gleichzeitige Alternative der synchronisierten List implementiert die List-Schnittstelle und ist Teil des Pakets java.util.concurrent und eine thread-sichere Sammlung.

public class CopyOnWriteArrayList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable

CopyOnWriteArrayList ist ausfallsicher und löst keine ConcurrentModificationException aus, wenn die zugrunde liegende CopyOnWriteArrayList während der Iteration geändert wird. Verwenden Sie eine separate Kopie von ArrayList.

Dies ist normalerweise zu kostspielig, da bei jedem Aktualisierungsvorgang eine geklonte Kopie erstellt wird. CopyOnWriteArrayList ist die beste Wahl nur für häufige Lesevorgänge.

/**
         * Returns a shallow copy of this list.  (The elements themselves
         * are not copied.)
         *
         * @return a clone of this list
         */
        public Object clone() {
            try {
                @SuppressWarnings("unchecked")
                CopyOnWriteArrayList<E> clone =
                    (CopyOnWriteArrayList<E>) super.clone();
                clone.resetLock();
                return clone;
            } catch (CloneNotSupportedException e) {
                // this shouldn't happen, since we are Cloneable
                throw new InternalError();
            }
        }
Vaquar Khan
quelle