【问题标题】:In java8, how to set the global value in the lambdas foreach block?java8中,如何在lambdas foreach块中设置全局值?
【发布时间】:2015-12-07 22:14:25
【问题描述】:
    public void test(){
       String x;
       List<String> list=Arrays.asList("a","b","c","d");

       list.forEach(n->{
          if(n.equals("d"))
            x="match the value";
       });
    }

1.和上面的代码一样,我想在foreach块旁边设置一个变量的值,可以吗?

2. 为什么?

3.foreach迭代器是有序还是无序?

4.我认为 lamdas foreach 块对于迭代器来说既酷又简单,但这确实是一件复杂的事情,而不是在 java 7 或更早版本中的相同工作。

【问题讨论】:

  • 您的循环可以替换为(例如)if (list.stream().filter("d"::equals).findAny().isPresent()) x = "match the value";。在大多数情况下,将有一种无需操作外部变量即可编写代码的方法。
  • @assylias: if(list.stream().anyMatch("d"::equals)) …if(list.contains("d")) …
  • @Holger Argh - 我总是忘记那个方法(anyMatch),是的,确实包含更好。谢谢!
  • 只使用旧的 for-each。
  • @All 谢谢。事实上,我想解决这个问题。 Link

标签: java foreach lambda java-8


【解决方案1】:

除了已经提供的惯用示例之外,另一种技巧是使用 AtomicReference,但我仅在您确实需要 'forEach' 并且更喜欢比真正功能变体更具可读性的情况下推荐它:

public void test(){
    AtomicReference<String> x = new AtomicReference<>();
    List<String> list= Arrays.asList("a", "b", "c", "d");

    list.forEach(n->{
        if(n.equals("d"))
            x.set("match the value");
    });
}

【讨论】:

  • 只为AtomicReference 投了赞成票,尽管我不推荐它用于 foreach。相反,如果用于并行流,这种方法是有意义的。
【解决方案2】:
  1. 不,你不能这样做。 (虽然你应该自己尝试过)
  2. 因为匿名内部类和 lambda 表达式中使用的变量必须是 effectively final
  3. 您可以使用filtermap 更简洁地实现同样的目的。

    Optional<String> d = list.stream()
                             .filter(c -> c.equals("d"))
                             .findFirst()
                             .map(c -> "match the value");
    

【讨论】:

    【解决方案3】:

    当然,您可以通过一个技巧“使外部值可变”:

    public void test() {
        String[] x = new String[1];
        List<String> list = Arrays.asList("a", "b", "c", "d");
    
        list.forEach(n -> {
            if (n.equals("d"))
                x[0] = "match the value";
        });
    }
    

    不过,请准备好被团队中的功能纯粹主义者殴打。然而,更好的是使用更实用的方法 (similar to Sleiman's approach):

    public void test() {
        List<String> list = Arrays.asList("a", "b", "c", "d");
        String x = list.stream()
                       .filter("d"::equals)
                       .findAny()
                       .map(v -> "match the value")
                       .orElse(null);
    }
    

    【讨论】:

    • 抱歉,我不得不投反对票。不建议为可以通过传统循环直接完成的事情发明一个高度复杂的功能结构。
    • @RalfKleberhoff:适合自己。
    【解决方案4】:

    正如已经解释过的,您不能从 lambda 主体(以及匿名类主体)修改外部方法的局部变量。我的建议是在完全没有必要时不要尝试使用 lambda。你的问题可以这样解决:

    public void test(){
       String x;
       List<String> list = Arrays.asList("a","b","c","d");
       if(list.contains("d"))
           x = "match the value";
    }
    

    一般来说,lambdas 是函数式编程的好朋友,在这种编程中你很少有可变变量(每个变量只分配一次)。如果你使用 lambdas,但继续以命令式的方式思考,你总会遇到这样的问题。

    【讨论】:

    • 附带说明,您的代码确实与 OP 的代码匹配,因为 x 之后完全没用,因为可能没有分配。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-02
    • 2023-04-06
    • 2021-02-02
    • 2020-10-29
    • 2017-04-23
    • 2017-11-30
    相关资源
    最近更新 更多