简体   繁体   English

带有多个语句的 Java 8 Lambda Stream forEach

[英]Java 8 Lambda Stream forEach with multiple statements

I am still in the process of learning Lambda, please excuse me If I am doing something wrong我还在学习Lambda中,如果我做错了什么,请见谅

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));

Seems like forEach can be executed for one statement only.似乎forEach只能为一个语句执行。 It doesn't return updated stream or function to process further.它不会返回更新的流或函数来进一步处理。 I might have selected wrong one altogether.我可能完全选错了。

Can someone guide me how to do this effectively?有人可以指导我如何有效地做到这一点吗?

One more question,还有一个问题,

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?

How do I convert this to Lambda expression?如何将其转换为 Lambda 表达式?

Forgot to relate to the first code snippet. 忘了与第一个代码片段相关联。 I wouldn't use forEach at all. 我根本不会使用forEach Since you are collecting the elements of the Stream into a List , it would make more sense to end the Stream processing with collect . 由于您要将Stream的元素收集到List ,因此使用collect结束Stream处理会更有意义。 Then you would need peek in order to set the ID. 然后你需要peek才能设置ID。

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

For the second snippet, forEach can execute multiple expressions, just like any lambda expression can : 对于第二个片段, forEach可以执行多个表达式,就像任何lambda表达式一样:

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

However (looking at your commented attempt), you can't use filter in this scenario, since you will only process some of the entries (for example, the entries for which entry.getA() == null ) if you do. 但是(查看您的注释尝试),您无法在此方案中使用过滤器,因为您只会处理某些条目(例如, entry.getA() == null的条目)。

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());
  }
});

In the first case alternatively to multiline forEach you can use the peek stream operation: 在第一种情况下,替换为multiline forEach您可以使用peek stream操作:

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

In the second case I'd suggest to extract the loop body to the separate method and use method reference to call it via forEach . 在第二种情况下,我建议将循环体提取到单独的方法,并使用方法引用通过forEach调用它。 Even without lambdas it would make your code more clear as the loop body is independent algorithm which processes the single entry so it might be useful in other places as well and can be tested separately. 即使没有lambdas,它也会使你的代码更加清晰,因为循环体是处理单个条目的独立算法,所以它在其他地方也可能有用,并且可以单独测试。

Update after question editing . 问题编辑后更新 if you have checked exceptions then you have two options: either change them to unchecked ones or don't use lambdas/streams at this piece of code at all. 如果你检查了异常,那么你有两个选择:要么将它们更改为未经检查的,要么根本不在这段代码中使用lambdas / streams。

You don't have to cram multiple operations into one stream/lambda. 您不必将多个操作塞入一个流/ lambda。 Consider separating them into 2 statements (using static import of toList() ): 考虑将它们分成2个语句(使用toList()静态导入):

entryList.forEach(e->e.setTempId(tempId));

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

My preferred approach is to concatenate java.util.function.Consumer<?> with andThen(...) .我的首选方法是将java.util.function.Consumer<?>andThen(...)

Applied to your example, it can be written in:应用于您的示例,它可以写成:

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

seen this way, is not that nice to read... but you can move the 2 consumers into local function variables and it would become as nice as:这样看,读起来不太好……但是您可以将 2 个使用者移动到局部函数变量中,它会变得像:

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奖金

You may write your combinator of consumers like:您可以编写您的消费者组合器,例如:

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

and it will allow to re-write the above example in:它将允许将上面的示例重写为:

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

I wish Consumer class had a method to combine multiple consumers of the same type, maybe this combine already exists somewhere and I'm not aware of it, so I invite you to search 😅我希望Consumer类有一个方法可以组合多个同类型的consumer,可能这个combine已经存在某处而我不知道,所以我邀请你搜索😅

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM