web-dev-qa-db-fra.com

comment supprimer un objet de stream dans la méthode foreach?

je dois tableaux: arrA et arrB. arrA et arrB sont des listes d'objets de types différents et la fonction add convertit les objets A en objets B. Je veux ajouter chaque objet d’arrA à arrB et supprimer cet objet d’arrA. J'essaie de le faire par flux:

arrA.stream().foreach(c -> {arrB.add(c); arrA.remove(c);});

quand j'exécute ceci, deux choses se passent:

  1. tous les objets ne sont pas passés d’arrA à arrB.
  2. après quelques itérations, une exception de pointeur nul est levée.

c'est parce que la longueur du tableau est réduite après chaque appel remove() et que le compteur d'itérations est augmenté (seuls les objets sous les index impairs sont passés à arrB)

Maintenant, je pourrais résoudre ce problème en copiant un tableau dans un appel de flux, puis en supprimant des objets dans un second appel de flux, mais cela ne semble pas correct pour moi.

Quelle serait la solution appropriée à ce problème?

EDIT . Informations complémentaires: En implémentation réelle, cette liste si déjà filtrée

arrA.stream().filter(some condition).foreach(c -> {arrB.add(c); arrA.remove(c);});

et on lui a demandé à quelques reprises d'ajouter des éléments répondant à différentes conditions à différentes listes (arrC, arrD etc.), mais chaque objet ne peut figurer que sur une liste.

7
Akka Jaworek

Les flux sont conçus pour être utilisés de manière plus fonctionnelle, traitant de préférence vos collections comme étant immuables.

La voie non-stream serait:

arrB.addAll(arrA);
arrA.clear();

Cependant, vous utiliserez peut-être Streams pour pouvoir filtrer l’entrée afin que cela ressemble davantage à:

arrB.addAll(arrA.stream().filter(x -> whatever).toList())

puis retirez de arrA (merci à @Holgar pour le commentaire).

arrA.removeIf(x -> whatever)

Si votre prédicat est cher, vous pouvez partitionner:

Map<Boolean, XXX> lists = arrA.stream()
  .collect(Collectors.partitioningBy(x -> whatever));
arrA = lists.get(false);
arrB = lists.get(true);

ou faites une liste des changements:

List<XXX> toMove = arrA.stream().filter(x->whatever).toList();
arrA.removeAll(toMove);
arrB.addAll(toMove);
15
davidsheldon

Comme d'autres l'ont mentionné, cela n'est pas possible avec foreach - car il est impossible avec la boucle for (A a: arrA) de supprimer des éléments.

À mon avis, la solution la plus propre consiste à utiliser un simple while avec itérateurs - les itérateurs vous permettent de supprimer des éléments lors d'une itération (tant que la collection le permet.

Iterator<A> it = arrA.iterator()
while (it.hasNext()) {
    A a = it.next();
    if (!check(a))
        continue;
    arrB.add(a);
    it.remove();
}

Cela vous évite également de copier/cloner arrA.

4
Martin Nyolt

Je ne pense pas que vous puissiez retirer de l'arrA pendant que vous le parcourez.

Vous pouvez contourner cela en l'enveloppant dans une nouvelle ArrayList <> ();

new ArrayList <> (arrA) .stream (). foreach (c -> {arrB.add (c); arrA.remove (c);});

1
Mike Samaras

je suppose que c'est parce que la longueur du tableau est réduite après chaque appel de remove () et que le compteur d'itérations est augmenté

Droite. la boucle for-each est comme une boucle for normale, mais plus facile à écrire et à lire. Vous pouvez le considérer comme un sucre syntaxique. En interne, il utilisera un Iterator ou des index de tableau. La méthode forEach de flux est une version plus sophistiquée de celle-ci qui permet une exécution parallèle et un style de codage fonctionnel, mais a ses propres inconvénients .

Comme pour toute boucle indexée, la suppression d'un élément en boucle interrompt la boucle. Pensez à avoir trois éléments d'indices 0, 1 et 2. Lorsque vous supprimez l'élément 0 lors de la première itération, les éléments de la liste seront décalés d'un élément et la prochaine itération, les éléments 0 (précédemment 1) et 1 (précédemment 2) . Votre variable de boucle pointe maintenant sur 1, donc elle saute l'élément réellement suivant. Lorsqu'il arrive à l'index 2, la boucle sur laquelle vous travaillez n'a plus qu'un élément (vous en avez supprimé deux), ce qui génère une erreur, car l'index est hors limites.

Solutions possibles:

  • Utilisez les méthodes List pour cloner et effacer des listes.
  • Faites-le avec deux boucles si vous avez vraiment besoin d'appeler les méthodes sur chaque élément.
1
Hubert Grzeskowiak