Java 8 Lambda Stream voor elk met meerdere statements

Ik ben nog bezig met het leren van Lambda, excuseer me als ik iets verkeerd doe

final Long tempId = 12345L;
List<Entry> updatedEntries = new LinkedList<>();
for (Entry entry : entryList) {
    entry.setTempId(tempId);
    updatedEntries.add(entityManager.update(entry, entry.getId()));
}
//entryList.stream().forEach(entry -> entry.setTempId(tempId));

Het lijkt erop dat forEachslechts voor één instructie kan worden uitgevoerd. Het retourneert geen bijgewerkte stream of functie om verder te verwerken. Ik heb misschien helemaal de verkeerde gekozen.

Kan iemand me helpen hoe ik dit effectief kan doen?

Nog een vraag,

public void doSomething() throws Exception {
    for(Entry entry: entryList){
        if(entry.getA() == null){
            printA() throws Exception;
        }
        if(entry.getB() == null){
            printB() throws Exception;
        }
        if(entry.getC() == null){
            printC() throws Exception;
        }
    }
}
    //entryList.stream().filter(entry -> entry.getA() == null).forEach(entry -> printA()); something like this?

Hoe converteer ik dit naar Lambda-expressie?


Antwoord 1, autoriteit 100%

Vergeten te verwijzen naar het eerste codefragment. Ik zou forEachhelemaal niet gebruiken. Aangezien u de elementen van de Streamverzamelt in een List, zou het logischer zijn om de Stream-verwerking te beëindigen met collect. Dan heb je peeknodig om de ID in te stellen.

List<Entry> updatedEntries = 
    entryList.stream()
             .peek(e -> e.setTempId(tempId))
             .collect (Collectors.toList());

Voor het tweede fragment kan forEachmeerdere expressies uitvoeren, net zoals elke lambda-expressie kan:

entryList.forEach(entry -> {
  if(entry.getA() == null){
    printA();
  }
  if(entry.getB() == null){
    printB();
  }
  if(entry.getC() == null){
    printC();
  }
});

U kunt echter (gezien uw poging met commentaar) geen filter gebruiken in dit scenario, omdat u slechts enkele van de vermeldingen verwerkt (bijvoorbeeld de vermeldingen waarvoor entry.getA() == null) als je dat doet.


Antwoord 2, autoriteit 27%

List<String> items = new ArrayList<>();
items.add("A");
items.add("B");
items.add("C");
items.add("D");
items.add("E");
//lambda
//Output : A,B,C,D,E
items.forEach(item->System.out.println(item));
//Output : C
items.forEach(item->{
    System.out.println(item);
    System.out.println(item.toLowerCase());
  }
});

Antwoord 3, autoriteit 8%

In het eerste geval kunt u als alternatief voor forEachmet meerdere regels de streambewerking peekgebruiken:

entryList.stream()
         .peek(entry -> entry.setTempId(tempId))
         .forEach(updatedEntries.add(entityManager.update(entry, entry.getId())));

In het tweede geval raad ik aan om de hoofdtekst van de lus naar de afzonderlijke methode te extraheren en de methodereferentie te gebruiken om deze aan te roepen via forEach. Zelfs zonder lambda’s zou het je code duidelijker maken, omdat de lusbody een onafhankelijk algoritme is dat de enkele invoer verwerkt, dus het kan ook op andere plaatsen nuttig zijn en afzonderlijk worden getest.

Update na bewerken van vragen. als je uitzonderingen hebt aangevinkt, heb je twee opties: verander ze in niet-aangevinkte of gebruik helemaal geen lambdas/streams bij dit stukje code.


Antwoord 4, autoriteit 8%

Je hoeft niet meerdere bewerkingen in één stream/lambda te proppen. Overweeg ze te scheiden in 2 instructies (met behulp van statische import van toList()):

entryList.forEach(e->e.setTempId(tempId));
List<Entry> updatedEntries = entryList.stream()
  .map(e->entityManager.update(entry, entry.getId()))
  .collect(toList());

Antwoord 5

Mijn voorkeur gaat uit naar het samenvoegen van java.util.function.Consumer<?>met andThen(...).

Toegepast op uw voorbeeld, kan het worden geschreven in:

entryList.stream()
  .forEach(
    ((Consumer<Entry>) entry -> entry.setTempId(tempId))
    .andThen(entry -> updatedEntries.add(entityManager.update(entry, entry.getId())))
  );

op deze manier gezien, is niet zo leuk om te lezen… maar je kunt de 2 consumenten verplaatsen naar lokale functievariabelen en het zou zo mooi worden als:

Consumer<Entry> c1 = entry -> entry.setTempId(tempId);
Consumer<Entry> c2 = entry -> updatedEntries.add(entityManager.update(entry, entry.getId()));
entryList.stream().forEach(c1.andThen(c2));

Bonus

U kunt uw combinator van consumenten schrijven als:

public static <T> Consumer<T> combine(Consumer<? super T>... consumers){
  return (T t) ->   Arrays.stream(consumers).forEach(c -> c.accept(t));
}

en het zal het mogelijk maken om het bovenstaande voorbeeld te herschrijven in:

entryList.stream()
  .forEach(combine(
    entry -> entry.setTempId(tempId),
    entry -> updatedEntries.add(entityManager.update(entry, entry.getId()))));

Ik wou dat de klasse Consumereen methode had om meerdere consumenten van hetzelfde type te combineren, misschien bestaat deze combineal ergens en ben ik me er niet van bewust, dus ik nodigen u uit om te zoeken 😅

Other episodes