it-swarm.com.de

Kann filter-> forEach-> nicht in einem Stream sammeln?

Ich möchte so etwas erreichen:

items.stream()
    .filter(s-> s.contains("B"))
    .forEach(s-> s.setState("ok"))
.collect(Collectors.toList());

filtern, dann eine Eigenschaft aus dem gefilterten Ergebnis ändern und das Ergebnis in einer Liste sammeln. Der Debugger sagt jedoch:

collect(Collectors.toList()) kann nicht für den primitiven Typ void aufgerufen werden.

Brauche ich dafür 2 Streams?

13
nimo23

Die forEach ist als Terminalbetrieb konzipiert und ja, Sie können nach dem Aufruf nichts mehr tun.

Der idiomatische Weg wäre, zuerst eine Transformation und dann collect() alles auf die gewünschte Datenstruktur anzuwenden. 

Die Transformation kann mit map durchgeführt werden, das für nicht mutierende Operationen ausgelegt ist.

Wenn Sie eine nicht mutierende Operation ausführen:

 items.stream()
   .filter(s -> s.contains("B"))
   .map(s -> s.withState("ok"))
   .collect(Collectors.toList());

dabei ist withState eine Methode, die eine Kopie des ursprünglichen Objekts einschließlich der bereitgestellten Änderung zurückgibt.


Wenn Sie eine Nebenwirkung haben:

items.stream()
  .filter(s -> s.contains("B"))
  .collect(Collectors.toList());

items.forEach(s -> s.setState("ok"))
13

Ersetzen Sie forEach durch map.

 items.stream()
      .filter(s-> s.contains("B"))
      .map(s-> {s.setState("ok");return s;})
      .collect(Collectors.toList());

forEach und collect sind beide Terminaloperationen - Streams müssen nur einen haben. Alles, was einen Stream<T> zurückgibt, ist ein intermediate operation, alles andere ist ein terminal operation

11
Eugene

Widerstehen Sie dem Drang, Nebenwirkungen ohne großen Grund aus dem Inneren des Streams zu ziehen. Erstellen Sie die neue Liste und übernehmen Sie die Änderungen:

List<MyObj> toProcess = items.stream()
    .filter(s -> s.contains("B"))
    .collect(toList());

toProcess.forEach(s -> s.setState("ok"));
4
Misha

forEach ist eine Terminaloperation, dh es wird kein Stream-Ergebnis erzeugt. forEach erzeugt nichts und collect gibt eine Collection zurück. Was Sie brauchen, ist eine Stream-Operation, die Elemente für Ihre Anforderungen ändert. Diese Operation ist map, mit der Sie eine Funktion angeben können, die auf jedes Element des Eingabestroms angewendet wird, und einen transformierten Strom von Elementen erzeugt. Also brauchst du so etwas wie:

items.stream()
     .filter (s -> s.contains("B"))
     .map    (s -> { s.setState("ok"); return s; }) // need to return a value here
     .collect(Collectors.toList());

Eine Alternative ist die Verwendung von peek, deren Zweck es ist, auf jedes durchlaufende Element eine Funktion anzuwenden.

items.stream()
     .filter (s -> s.contains("B"))
     .peek   (s -> s.setState("ok")) // no need to return a value here
     .collect(Collectors.toList());

Sie können nicht zwei Terminaloperationen auf demselben Stream ausführen.

Sie können den Status des Objekts in einer Zwischenoperation festlegen, z. B. map:

List<YourClass> list = 
    items.stream()
         .filter(s-> s.contains("B"))
         .map(s-> {
                      s.setState("ok"); 
                      return s;
                  })
         .collect(Collectors.toList());
2
Eran
 items.stream()
      .filter(s-> s.contains("B"))
      .peek(s-> s.setState("ok"))
      .collect(Collectors.toList());

Stream peek (Consumer-Aktion) Gibt einen Stream aus .__ zurück. der Elemente dieses Streams, zusätzlich das bereitgestellte Aktion für jedes Element, wenn Elemente aus dem resultierenden .__ verbraucht werden. Strom. Dies ist eine Zwischenoperation.

Bei parallelen Stream-Pipelines kann die Aktion an beliebiger Stelle aufgerufen werden Zeit und in welchem ​​Thread das Element von der .__ zur Verfügung gestellt wird. Upstream-Betrieb. Wenn die Aktion den gemeinsam genutzten Status ändert, lautet sie verantwortlich für die Bereitstellung der erforderlichen Synchronisation.

API-Hinweis: Diese Methode unterstützt hauptsächlich das Debuggen, wobei Sie möchten die Elemente sehen, wenn sie an einem bestimmten Punkt in einer .__ vorbei fließen. Pipeline:

 Stream.of("one", "two", "three", "four")
     .filter(e -> e.length() > 3)
     .peek(e -> System.out.println("Filtered value: " + e))
     .map(String::toUpperCase)
     .peek(e -> System.out.println("Mapped value: " + e))
     .collect(Collectors.toList());   Parameters: action - a non-interfering action to perform on the elements as they are consumed

aus dem Stream kehrt zurück: Der neue Stream

https://docs.Oracle.com/javase/8/docs/api/Java/util/stream/Stream.html#peek-Java.util.function.Consumer-

0
宏杰李