How can I use Java Stream to reduce with this class structure?

Viewed 62

This is the example of the class I'm working on

public class TestReduce
{
    private static Set<Integer> seed = ImmutableSet.of(1, 2);
    public static void main(String args[]) {
        List<Accumulator> accumulators = ImmutableList.of(new Accumulator(ImmutableSet.of(5, 6)), new Accumulator(ImmutableSet.of(7, 8)));

        accumulators.stream()
                .forEach(a -> {
                    seed = a.combineResult(seed);
                });

        System.out.println(seed);
    }
}

class Accumulator
{
    public Accumulator(Set<Integer> integers)
    {
        accumulatedNumbers = integers;
    }

    public Set<Integer> combineResult(Set<Integer> numbers) {
        // Do some manipulation for the numbers 
        return (the new numbers);
    }

    private Set<Integer> accumulatedNumbers;
}

I would like to reduce all of the Accumulators to just a set of numbers but with the initial value. However, I cannot change the signature of the method combineResult. In the example, I did this by just using forEach but I'm not sure if there's a cleaner way or java stream way to achieve this? I tried using reduce but I couldn't quite get the arguments of the reduce right.

1 Answers

(Answer for the original question)

This doesn't seem like a good approach. You're just unioning some sets.

If you can't change the signature of combineResult, you can do:

ImmutableSet<Integer> seed =
    Stream.concat(
        initialSet.stream(),
        accumulators.stream()
            // Essentially just extracting the set from each accumulator.
            // Adding a getter for the set to the Accumulator class would be clearer.
            .map(a -> a.combineResult(Collections.emptySet()))
            .flatMap(Set::stream))
        .collect(ImmutableSet.toImmutableSet());

For a generalized combineResult, you shouldn't use reduce, because that operation may be non-associative.

It's easy just to use a plain old loop in that case.

Set<Integer> seed = ImmutableSet.of(1, 2);
for (Accumulator a : accumulators) {
  seed = a.combineResult(seed);
}

This avoids the principal issue with your current approach, namely non-thread locality of the calculation state (that is, other threads and previous invocations of the loop cannot affect the current invocation).

Related