Gayan Weerakutti
Gayan Weerakutti

Reputation: 13831

Iterations required when chaining stream intermediate operations

I know that stream intermediate operations in Java are lazy and do not get executed until a terminal operation is being called. However, I'm not sure whether chaining intermediate operations increases the number of iterations required. Because sometimes we separate operations into multiple intermediate operations, just to make it easier to read.

So for instance the following two examples do the same thing. But in the second example, two intermediate operations are joined into one.

myStream
     .map(s -> methodA(s))
     .map(s -> methodB(s))
     .collect(Collectors.joining());
myStream
     .map(s -> methodB(methodA(s)))
     .collect(Collectors.joining());

Aren't the number of iterations required is same in both cases? Because it seems, it is possible for the JVM to execute it in a single iteration (similar to a single for-loop), rather than iterating over the elements for each intermediate operation.

Upvotes: 0

Views: 208

Answers (1)

WJS
WJS

Reputation: 40057

I know that stream intermediate operations in Java are lazy and do not get executed until a terminal operation is being called. However, I'm not sure whether chaining intermediate operations increases the number of iterations required. ... Aren't the number of iterations required is same in both cases?

The following seems to confirm your (and others) conclusions. I used AtomicInteger since the counts would be off using parallel streams. In no case did the total number of method calls differ. However, since the two methods return different values, the sums will be different due to the ordering of the maps. In the first case, MethodA is processed last so its value will be summed. In the second case, MethodB is processed last so its value will be summed.

static AtomicInteger count = new AtomicInteger(0);
Random r = new Random();
for (int i = 0; i < 10000; i++) {
    count.set(0);
    List<Integer> list = IntStream.generate(
            () -> 1)
            .limit(r.nextInt(10) + 1).boxed().toList();
    
    int sum1 = list.stream().mapToInt(s -> methodB(s)).map(s -> methodA(s))
            .sum();
    
    int save = count.get();
    
    count.set(0);
    int sum2 = list.stream().mapToInt(s -> methodB(methodA(s)))
              .sum();
    if (save != count.get()) {
        System.out.println(
                "Inconsistent counts: " + save + " " + count);
    }
}
    
public static int methodA(int v) {
    count.incrementAndGet();
    return 1;
}

    
public static int methodB(int v) {
    count.incrementAndGet();
    return 2;
}

Upvotes: 1

Related Questions