如何在Java流中累积对不可变对象的更改,并在流终止时获取最终结果

时间:2016-09-17 19:06:47

标签: java lambda java-stream

有没有办法用Java 8流和lambdas表达以下代码?

ImmutableObject immutable = new ImmutableObject();
for(int i = 0; i < 10; i++) {
    immutable = immutable.changeState(i);
}
return immutable;

ImmutableObject只是一个简单的不可变对象:

class ImmutableObject {
    private final int state;

    public ImmutableObject() {
        this(0);
    }

    public ImmutableObject(final int state) {
        this.state = state;

    }

    public ImmutableObject changeState(final int newState) {
        return new ImmutableObject(newState);
    }
}

我想出了这个:

IntStream.range(0, 10).
       mapToObj(i -> immutable.changeState(i)).reduce((a, b) -> b).get()

但是每次迭代的结果都被丢弃 - 我不能将结果分配回不可变引用,因为它在lamba表达式中并且应该(有效地)为final。

2 个答案:

答案 0 :(得分:1)

这是溪流不能解决所有事情的完美例子。

但是,如果您坚持,这里是一个流版本,在“holder”对象上使用collect(),因此可以更新参考值。

这是一个黑客,它很难看,但它会起作用。

为了使它成为MCVE,我创建了一个简单的不可变和实现。

public class Test {
    public static void main(String[] args) {
        ImmutableObject o = IntStream.range(0, 10).collect(
                () -> new ImmutableObject[] { new ImmutableObject(0) },
                (h, i) -> h[0] = h[0].changeState(i),
                (h1, h2) -> { throw new UnsupportedOperationException("Parallel not supported"); }
        )[0];
        System.out.println(o); // prints: 45
    }
}

class ImmutableObject {
    private final int v;
    ImmutableObject(int v) { this.v = v; }
    ImmutableObject changeState(int i) { return new ImmutableObject(this.v + i); }
    @Override public String toString() { return Integer.toString(this.v); }
}

答案 1 :(得分:1)

让我们一步一步来做。首先用流替换循环。

private static Immutable immutable = new Immutable(0);
public static void main(String[] args) {
    IntStream.range(0,10).forEach(i -> {
            Immutable localImmutable = immutable.changeState(i);
            System.out.println(localImmutable.toString());
        });
}

它在1..10中为i执行,因为range()具有独占上限。

接下来,您希望存储引用新的Immutable实例(代码中的ImmutableObject)。您可以在lambda(或更广泛意义上的任何Java闭包)中访问最终(或有效最终)变量。也许您已经听说过每个lambda(或闭包)都引用了它的上下文。我们可以使用此上下文来访问静态字段。我们来试试吧

private static Immutable immutable = new Immutable(0);
public static void main(String[] args) {
    IntStream.range(0,10).forEach(i -> {
            immutable = immutable.changeState(i);
            System.out.println(immutable.toString());
        });
}

由于上下文无法改变其有效的最终结果(在某些语言中 - 如groovy - 您可以通过设置适当的委托来选择不同的上下文)。最后但并非最不重要的是让我们摆脱静态关键字。我结束了示例

import java.util.stream.IntStream;

public class Test {
    private static class Immutable {
        private static int counter = 0;
        private final int id = ++counter;

        private final int state;

        public Immutable(int state) {
            this.state = state;
        }

        public Immutable changeState(int i) {
            return new Immutable(i);
        }

        @Override
        public String toString() {
            return "id: " + id + ", state: " + state;
        }
    }

    private Immutable immutable = new Immutable(0);
    public void run() {
        IntStream.range(0,10).forEach(i -> {
                immutable = immutable.changeState(i);
                System.out.println(immutable.toString());
            });
    }

    public static void main(String[] args) {
        new Test().run();
    }
}

如果你想松开私人领域,你可以使用

public static void main(String[] args) {
    Optional<Immutable> last = IntStream.range(0,10).boxed()
        .map(Immutable::new)
        .reduce((a,b) -> b);

    System.out.println(last.get());
}