web-dev-qa-db-fra.com

Java 8 applique la fonction à tous les éléments de Stream sans rompre la chaîne de flux

Existe-t-il un moyen Java d’appliquer une fonction à tous les éléments d’un Stream sans rompre la chaîne Stream?? Je sais que je peux appeler forEach, mais cette méthode retourne un void, pas un Stream.

27
alexgbelov

Il y a (au moins) 3 façons. Par souci d’exemple de code, j’ai supposé que vous souhaitiez appeler deux méthodes consommateur methodA et methodB:

A. Utilisez peek():

list.stream().peek(x -> methodA(x)).forEach(x -> methodB(x));

Bien que les docs disent l’utiliser seulement pour "déboguer", cela fonctionne (et il est en production actuellement)

B. Utilisez map() pour appeler la méthodeA, puis renvoyez l'élément dans le flux:

list.stream().map(x -> {method1(x); return x;}).forEach(x -> methodB(x));

C'est probablement l'approche la plus "acceptable".

C. Faites deux choses dans la forEach():

list.stream().forEach(x -> {method1(x); methodB(x);});

C'est le moins flexible et peut ne pas convenir à vos besoins.

28
Bohemian

Vous recherchez la fonction map() de Stream.

exemple:

List<String> strings = stream
.map(Object::toString)
.collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
4
csenga

La meilleure option consiste à appliquer une carte à votre flux. qui retourne un flux constitué des résultats de l'application de la fonction donnée aux éléments de ce flux, par exemple:

IntStream.rangeClosed(40, 70).limit(20).mapToObj(i -> (Integer) i).map(item->item+3).map(item->item*2)... 

(Flux d'éléments de type StreamItemABC)

Nous appliquons plusieurs modifications au flux mais de cette façon nous ne pouvons pas transmettre d’arguments à la méthode map, pour le corriger:

private void applyMethod(ParameterX parX) {
 someStreamX().map(n -> methodX(n, parX))...
}

private StreamItemABC methodX (StreamItemABC item, ParameterX parX) {
  return notification;
}
1
Mr.Q

Pas tout à fait sûr de ce que vous entendez par breaking the stream chain, mais toute opération sur un Stream qui retourne un Stream ne sera pas pause ou consommera votre flux. Les flux sont consommés par terminal operations et comme vous l'avez noté forEachne retourne pas a Stream<T> et en tant que tel termine le flux en exécutant toutes les opérations intermediate avant les forEach et les forEach lui-même.

Dans l'exemple que vous avez fourni dans les commentaires:

 myStream.map(obj -> {obj.foo(); return obj;}

Vous ne pouvez pas vraiment faire cela avec un seul paquebot. Bien sûr, vous pouvez utiliser une référence de méthode, mais votre Stream retourné serait alors d'un type différent (en supposant que foo renvoie un type):

  myStream.map(Obj::foo) // this will turn into Stream<T>, where T is 
           // the return type of foo, instead of Stream<Obj>

De plus, votre opération map est stateful, ce qui est fortement déconseillé. Votre code sera compilé et fonctionnera peut-être comme vous le souhaitez, mais il pourrait échouer ultérieurement. map les opérations doivent être stateless.

1
Eugene

Je pense que vous recherchez Stream.peek . Mais lisez attentivement la documentation, car elle a été conçue principalement comme une méthode de débogage. De la docs:

Cette méthode existe principalement pour prendre en charge le débogage, où vous souhaitez voir les éléments lorsqu'ils passent au-delà d'un certain point dans un pipeline.

L'action passée à peek doit être non gênante .

Je pense que le moyen le plus propre est d'ajouter un mutateur aux objets du flux.

Par exemple,

class Victim {
   private String tag;
   private Victim withTag(String t)
      this.tag = t;
      return this;
   }
}

List<Victim> base = List.of(new Victim());
Stream<Victim> transformed = base.stream().map(v -> v.withTag("myTag"));

Si vous préférez (et beaucoup le voudront), vous pouvez faire en sorte que la méthode withTag crée et renvoie une nouvelle victime. Cela vous permet de rendre la victime immuable.

0
Roger Hayes