Existe-t-il une liste simultanée dans le JDK de Java?

277

Comment puis-je créer une instance de liste simultanée, où je peux accéder aux éléments par index? Le JDK a-t-il des classes ou des méthodes d'usine que je peux utiliser?

AlikElzin-kilaka
la source
28
Pourquoi pas constructif? Plusieurs CopyOnWriteArrayList proposés qui ne se trouvent pas dans .Net. Vous pouvez dire que les deux questions sont liées mais ne pas fermer celle-ci !!!
AlikElzin-kilaka
1
Je ne sais pas pourquoi Jarrod Roberson penserait que c'était une bonne idée de reprendre les modifications détaillées apportées par Stephan et de les ramener à la question originale et mal formulée. La réponse de Jarrod est toujours parfaitement acceptable. En fait, CopyOnWriteArrayList est la seule classe simultanée implémentant List dans le JDK. Intrigué ...
Matt Passell
10
Parce que la réponse acceptée était à la question d'origine et Stephan a posé une question complètement indépendante avec un tas de code source que l' affiche originale n'incluait nulle part de changer complètement la question, ce qui a généré plus de réponses qui suggéraient des choses autres que celles Listque l'original spécifiquement dit que c'est une exigence qui est considérée comme du vandalisme. Un modérateur a déjà verrouillé la question à cause des personnes qui se plaignent que les réponses ne répondent pas à cette version vandalisée de la question.
1
/ locked/ closed/ commentaire précédent
8
Il n'y a aucune raison de clore cette question. Il pose des questions sur les classes du JDK, ce qui n'a rien à voir avec la recherche d'une bibliothèque; c'est la base de Java.
maaartinus

Réponses:

175

Il existe une implémentation de liste simultanée dans java.util.concurrent . CopyOnWriteArrayList en particulier.

Basil Bourque
la source
84
Notez qu'il copie la liste entière sur chaque insert, il est donc souvent inefficace.
dfrankow
22
@dfrankow Mais il peut plus plus efficace si nous parcourons beaucoup plus que vous mettez à jour.
b1nary.atr0phy
Ne fonctionne pas bien comme indiqué ici. J'ai des exceptions même si j'utilise simplement sa méthode addAll et le lis en utilisant stream. stackoverflow.com/questions/1527519/…
devssh
166

Si vous ne vous souciez pas d'avoir un accès basé sur un index et que vous voulez simplement les caractéristiques préservant l'ordre d'insertion d'une liste, vous pouvez envisager un java.util.concurrent.ConcurrentLinkedQueue . Puisqu'il implémente Iterable, une fois que vous avez terminé d'ajouter tous les éléments, vous pouvez parcourir le contenu en utilisant la syntaxe améliorée pour:

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

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

for (String href : globalQueue) {
    //do something with href
}
Matt Passell
la source
4
Je pense que le simplifié pour la déclaration ( :) s'appelle foreach: docs.oracle.com/javase/1.5.0/docs/guide/language/foreach.html
AlikElzin-kilaka
2
@ AlikElzin-kilaka Vous avez raison. Je pense que ce nom m'a toujours dérangé, car la syntaxe réelle n'inclut pas le mot "chacun", mais je mettrai à jour la réponse pour utiliser le nom officiel. :)
Matt Passell
3
@ AlikElzin-kilaka Nitpicking, mais selon la version 8 de JLS, cela s'appelle le "Enhanced for Statement". La même chose dans le tutoriel java .
Roland
1
@Roland n'est certainement PAS tatillonne. Il y a (maintenant) une différence entre "pour chaque" et "amélioré pour" en Java.
hfontanez
2
@Roland indirectement. Je crois qu'ils ont renommé "pour chaque boucle" en "amélioré pour" pour éliminer la confusion entre Stream.forEach et ce qui est maintenant connu comme amélioré pour.
hfontanez
128

Vous pouvez très bien utiliser Collections.synchronizedList (List) si tout ce dont vous avez besoin est une simple synchronisation d'invocation:

 List<Object> objList = Collections.synchronizedList(new ArrayList<Object>());
Yanick Rochon
la source
70
Le résultat de synchronizedListest "synchronisé" mais pas "simultané". Un problème fondamental que de nombreuses opérations List - qui sont basées sur un index - ne sont pas elles-mêmes atomiques et doivent faire partie d'une construction d'exclusion mutuelle plus large.
6
OMI, asing a Vectorest plus simple que Collections.synchronizedList(new ArrayList<Object>()).
Stephan
8
Le résultat de synchronizedList est "synchronized" mais pas "concurrent".
Kanagavelu Sugumar
46

Parce que l'acte d'acquérir la position et d'obtenir l'élément à partir de la position donnée nécessite naturellement un certain verrouillage (vous ne pouvez pas avoir la liste de changements structurels entre ces deux opérations).

L'idée même d'une collection simultanée est que chaque opération en elle-même est atomique et peut être effectuée sans verrouillage / synchronisation explicite.

Par conséquent, obtenir l'élément en position à npartir d'une donnée en Listtant qu'opération atomique n'a pas trop de sens dans une situation où un accès simultané est prévu.

Joachim Sauer
la source
6
Joachim, je pense que vous avez frappé le clou sur la tête. Prenons par exemple une liste en lecture seule comme liste simultanée. Obtenir l'élément à la position N de la liste est non seulement logique, mais c'est en résumé le problème. Ainsi, une liste immuable (minuscule L) serait un bon exemple, mais ce n'est pas une liste (majuscule L). CopyOnWriteArrayList est simultané, mais beaucoup de gens n'aiment pas les performances. Une solution sur le modèle des cordes (cordes à cordes) serait probablement un bon gagnant.
johnstosh
1
Très bon point. Mais la liste que l'OP va utiliser peut avoir une utilisation très spécifique. Par exemple, il peut être rempli dans un environnement simultané, puis "verrouillé" (quoi que cela signifie), puis accessible en toute sécurité par index. Ainsi, dans la première phase de remplissage d'une telle liste, il faudra toujours une implémentation thread-safe. Malheureusement, le PO n'était pas précis sur la façon dont la liste qu'il recherche sera utilisée.
igor.zh
13

Vous avez ces options:

  • Collections.synchronizedList(): vous pouvez encapsuler n'importe quelle Listimplémentation ( ArrayList, LinkedListou une liste tierce). L'accès à chaque méthode (lecture et écriture) sera protégé à l'aide de synchronized. Lorsque vous utilisez iterator()ou améliorez la boucle, vous devez synchroniser manuellement; lors de l'itération, les autres threads sont complètement bloqués, même en lecture. Vous pouvez également synchroniser séparément pour chacun hasNextet les nextappels, mais ConcurrentModificationExceptionc'est possible.

  • CopyOnWriteArrayList: c'est cher à modifier, mais sans attendre pour lire. Les itérateurs ne jettent jamais ConcurrentModificationException, ils retournent un instantané de la liste au moment de la création de l'itérateur, même si la liste est modifiée par un autre thread pendant l'itération. Utile pour les listes rarement mises à jour. Les opérations en bloc comme addAllsont préférées pour les mises à jour - la matrice interne est copiée moins de fois.

  • Vector: très similaire synchronizedList, mais l'itération est également synchronisée. Cependant, les itérateurs peuvent lancer ConcurrentModificationException, si le vecteur est modifié par un autre thread pendant l'itération.

Autres options:

  • Collections.unmodifiableList(): sans verrouillage, thread-safe, mais non modifiable
  • Queueou Dequepeut être une alternative si vous ajoutez / supprimez uniquement à la fin de la liste et parcourez la liste. Il n'y a pas d'accès par index et pas d'ajout / suppression à des endroits arbitraires. Ils ont plusieurs implémentations simultanées avec de meilleures performances et un meilleur accès simultané, mais cela dépasse le cadre de cette question. Vous pouvez également jeter un œil à JCTools , ils contiennent des implémentations de files d'attente plus performantes spécialisées pour un seul consommateur ou un seul producteur.
Oliv
la source
4

entrez la description de l'image ici

CopyOnWriteArrayList est une variante thread-safe d'ArrayList dans laquelle toutes les opérations de mutation (ajout, définition, etc.) sont implémentées en effectuant une nouvelle copie du tableau sous-jacent.

CopyOnWriteArrayList est une alternative simultanée de List synchronise les implémentations de l'interface List et de sa partie du paquet java.util.concurrent et de sa collection thread-safe.

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

CopyOnWriteArrayList est à sécurité intégrée et ne lève pas ConcurrentModificationException lorsque CopyOnWriteArrayList sous-jacent est modifié pendant l'itération, utilisez une copie distincte d'ArrayList.

Ceci est généralement trop coûteux car le tableau de copie impliquait chaque opération de mise à jour, une copie clonée sera créée. CopyOnWriteArrayList est le meilleur choix uniquement pour les opérations de lecture fréquentes.

/**
         * 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
la source