Je commence tout juste à travailler avec des listes en Java. Je me demande quelle est la méthode recommandée pour modifier chaque élément d'une liste?
J'ai été capable de le faire avec les deux méthodes suivantes, mais les deux semblent assez peu élégantes. Y a-t-il une meilleure façon de faire cela en Java? Et l'une des méthodes ci-dessous est-elle recommandée par rapport à l'autre ou les deux sont-elles au même niveau?
//Modifying with foreach
for (String each : list)
{
list.set(list.indexOf(each), each+ " blah");
}
//Modifying with for
for (ListIterator<String> i = list.listIterator(); i.hasNext(); i.next())
{
i.next();
list.set(i.nextIndex()-1, i.previous() + " blah yadda");
}
La deuxième version serait mieux. En interne, ils sont identiques à la fin, mais le second vous permet en fait de modifier la liste, tandis que le premier lève une exception ConcurrentModificationException.
Mais alors vous utilisez l'Iterator de manière incorrecte. Voici comment vous le faites correctement:
for (final ListIterator<String> i = list.listIterator(); i.hasNext();) {
final String element = i.next();
i.set(element + "yaddayadda");
}
L'itérateur est celui qui a besoin de modifier la liste, car il est le seul à savoir comment le faire correctement, sans confusion, à propos des éléments de la liste et de leur ordre.
Edit: Parce que je le vois dans tous les commentaires et les autres réponses:
Pourquoi ne pas utiliser list.get, list.set et list.size dans une boucle
Il existe de nombreuses collections dans la structure de collections Java, chacune optimisée pour des besoins spécifiques. De nombreuses personnes utilisent ArrayList, qui utilise un tableau en interne. C’est bien tant que la quantité d’éléments ne change pas beaucoup dans le temps et présente l’avantage particulier que les opérations get, set et size sont des opérations à temps constant sur ce type de liste spécifique .
Il existe cependant d'autres types de liste, où ce n'est pas vrai. Par exemple, si vous avez une liste qui grossit et/ou se réduit constamment, il est préférable d’utiliser une liste LinkedList, car contrairement à ArrayList, add (élément) est une opération à temps constant, mais add (index, élément), get ( index) et supprimer (index) ne sont pas!
Pour obtenir la position de l'index spécifique, la liste doit être parcourue du premier/dernier jusqu'à ce que l'élément spécifique soit trouvé. Donc, si vous faites cela dans une boucle, cela équivaut au pseudo-code suivant:
for (int index = 0; index < list.size(); ++index) {
Element e = get( (for(int i = 0; i < size; ++i) { if (i == index) return element; else element = nextElement(); }) );
}
L'Iterator est un moyen abstrait de parcourir une liste et permet donc de s'assurer que le parcours est effectué de manière optimale pour chaque liste. Les tests montrent qu’il existe peu de décalage horaire entre l’utilisation d’un itérateur et d’obtenir (i) pour un ArrayList, mais un énorme décalage horaire (en faveur de l’itérateur) sur un LinkedList.
EDIT: Si vous savez que size()
, get(index)
et set(index, value)
sont toutes des opérations à temps constant pour les opérations que vous utilisez (par exemple, pour ArrayList
), personnellement, je ne ferais que sauter les itérateurs:
for (int i = 0; i < list.size(); i++) {
list.set(i, list.get(i) + " blah");
}
Votre première approche est inefficace et potentiellement incorrecte (étant donné que indexOf
peut renvoyer la mauvaise valeur - il renverra la correspondance première). Votre deuxième approche est très déroutante - le fait que vous appeliez next()
deux fois et previous
une fois rend difficile la compréhension, à mon avis.
Toute approche utilisant List.set(index, value)
sera inefficace pour une liste qui n'a pas d'accès en écriture indexé dans le temps constant, bien sûr. Comme TwoThe l'a noté, utiliser ListIterator.set(value)
est bien meilleur. L’approche consistant à utiliser une variable ListIterator
est une meilleure approche polyvalente.
Cela dit, dans de nombreux cas, une autre solution consisterait à modifier votre conception afin de projeter une liste sur une autre, en tant que vue ou de manière matérielle. Lorsque vous n'êtes pas changeant la liste, vous n'avez pas à vous en préoccuper.
J'ai le mien qui fonctionne de cette façon
String desiredInvoice="abc-123";
long desiredAmount=1500;
for (ListIterator<MyPurchase> it = input.getMyPurchaseList().listIterator(); it.hasNext();) {
MyPurchase item = it.next();
if (item.getInvoiceNo().equalsIgnoreCase(desiredInvoice)) {
item.setPaymentAmount(desiredAmount);
it.set(item);
break;
}
}
En interne, il existe dans Iterator
pour la mise en œuvre de for-each
. Il n'y a donc pas de déférence entre ces deux cas. Mais si vous essayez de modifier un élément, il va throws
ConcurrentModificationException
.