从 lambda 内部修改局部变量使用包装器

2022-08-31 06:53:51

修改 中的局部变量会产生编译错误:forEach

正常

    int ordinal = 0;
    for (Example s : list) {
        s.setOrdinal(ordinal);
        ordinal++;
    }

使用 Lambda

    int ordinal = 0;
    list.forEach(s -> {
        s.setOrdinal(ordinal);
        ordinal++;
    });

任何想法如何解决这个问题?


答案 1

使用包装器

任何一种包装都是好的。

Java 10+中,请使用此构造,因为它非常容易设置:

var wrapper = new Object(){ int ordinal = 0; };
list.forEach(s -> {
  s.setOrdinal(wrapper.ordinal++);
});

对于 Java 8+,请使用 AtomicInteger

AtomicInteger ordinal = new AtomicInteger(0);
list.forEach(s -> {
  s.setOrdinal(ordinal.getAndIncrement());
});

...或数组:

int[] ordinal = { 0 };
list.forEach(s -> {
  s.setOrdinal(ordinal[0]++);
});

注意:如果使用并行流,请非常小心。您可能最终得不到预期的结果。像斯图尔特这样的其他解决方案可能更适合这些情况。

对于非int

当然,这对于 除 以外的类型仍然有效。int

例如,使用Java 10 +

var wrapper = new Object(){ String value = ""; };
list.forEach(s->{
  wrapper.value += "blah";
});

或者,如果您坚持使用Java 89,请使用与我们上面相同的构造,但是使用AtomicReference...

AtomicReference<String> value = new AtomicReference<>("");
list.forEach(s -> {
  value.set(value.get() + s);
});

...或数组:

String[] value = { "" };
list.forEach(s-> {
  value[0] += s;
});

答案 2

这相当接近 XY 问题。也就是说,所问的问题本质上是如何从 lambda 中变异捕获的局部变量。但手头的实际任务是如何对列表的元素进行编号。

根据我的经验,在80%以上的时间里,存在如何从lambda内部变异捕获的本地的问题,有更好的方法可以继续。通常这涉及缩减,但在这种情况下,在列表索引上运行流的技术非常适用:

IntStream.range(0, list.size())
         .forEach(i -> list.get(i).setOrdinal(i));

推荐