Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Replacing traditional newForLoop with Java 8 Streams

So, finally making a relatively large jump from Java 6 to Java 8, I've read up a fair amount of Java 8 Streams API. Unfortunately, almost all the examples that have been asked are almost close to what I'm trying to figure out how to do, but not close enough.

What I have is

final List<Function<? super Double, Double>> myList = generateList();
final double myVal = calculate(10);

private double calculate(double val) {
    for (Function<? super Double, Double> function : this.myList) {
        val += function.apply(val);
    }
    return val;
}

Now, I've come to understand I could do something similar with .stream().forEach(), but that only applies the foreach and streams require final variables. I've tried to explore a bit with DoubleStream to get a sum(), but I'd need to re-apply the current sum to each Function and add that sum to the next function, as the code example above shows.

Is this possible with pure Stream API?

Edit: So after testing with the reduce() area, I ran a simple test on the time it takes to perform this type of calculation and the results aren't in favor of streams. Here's an example https://gist.github.com/gabizou/33f616c08bde5ab97e56. Included are the log outputs from the fairly basic test.

like image 469
gabizou Avatar asked Sep 01 '15 18:09

gabizou


2 Answers

You can use the stream API to compose a function out of your list of functions.

static List<Function<? super Double, Double>> myList
    = Arrays.asList(d -> d + 4, d -> d * 2, d -> d - 3);

static Function<Double, Double> total=myList.stream()
    .map(f -> (Function<Double, Double>) d -> d + f.apply(d))
    .reduce(Function::andThen).orElse(Function.identity());

static double calculate(double val) {
    return total.apply(val);
}

public static void main(String[] args) {
    System.out.println(calculate(10));
}

The stream operation which produces the composed function does not have the associativity problem and could in theory even run in parallel (though there is no benefit here) while the result is a single function which is per se sequential and never dissolved into parts which would need to be associative.

like image 83
Holger Avatar answered Oct 22 '22 21:10

Holger


Yes, you can use a stream solution, by performing a reduction:

private double calculate(double val) {
    return myList.stream().reduce(val, (d, f) -> d + f.apply(d), (a, b) -> a + b);
}

A reduction takes each element and aggregates (reduces) it to one value. There are 3 flavours of the reduce() method - the one used here does the trick.


Some test code:

static Function<? super Double, Double> a = (d) -> d + 4;
static Function<? super Double, Double> b = (d) -> d * 2;
static Function<? super Double, Double> c = (d) -> d - 3;
static List<Function<? super Double, Double>> myList = Arrays.asList(a, b, c);

static double calculate(double val) {
    return myList.stream().reduce(val, (d, f) -> d + f.apply(d), (a, b) -> a + b);
}

public static void main(String[] args) {
    System.out.println(calculate(10));
}

Output:

141.0
like image 43
Bohemian Avatar answered Oct 22 '22 21:10

Bohemian