I have been trying to find clear contract from official Java documentation as to in which order Java streams, once a terminal operation is called, process the elements and call intermediate operations.
For example lets look at these examples that use both Java stream version and plain iteration version (both producing same outcome) .
Example1:
List<Integer> ints = Arrays.asList(1, 2, 3, 4, 5);
Function<Integer, Integer> map1 = i -> i;
Predicate<Integer> f1 = i -> i > 2;
public int findFirstUsingStreams(List<Integer> ints){
return ints.stream().map(map1).filter(f1).findFirst().orElse(-1);
}
public int findFirstUsingLoopV1(List<Integer> ints){
for (int i : ints){
int mappedI = map1.apply(i);
if ( f1.test(mappedI) ) return mappedI;
}
return -1;
}
public int findFirstUsingLoopV2(List<Integer> ints){
List<Integer> mappedInts = new ArrayList<>( ints.size() );
for (int i : ints){
int mappedI = map1.apply(i);
mappedInts.add(mappedI);
}
for (int mappedI : mappedInts){
if ( f1.test(mappedI) ) return mappedI;
}
return -1;
}
Will the Java stream in findFirstUsingStreams method after findFirst is called run map1 in the order described in findFirstUsingLoopV1 (map is not run for all elements) or as described in findFirstUsingLoopV2 (map is run for all elements)?
And will that order change in future versions of Java or there is an official documentation that guarantees us the order of map1 calls?
Example2:
Predicate<Integer> f1 = i -> i > 2;
Predicate<Integer> f2 = i -> i > 3;
public List<Integer> collectUsingStreams(List<Integer> ints){
return ints.stream().filter(f1).filter(f2).collect( Collectors.toList() );
}
public List<Integer> collectUsingLoopV1(List<Integer> ints){
List<Integer> result = new ArrayList<>();
for (int i : ints){
if ( f1.test(i) && f2.test(i) ) result.add(i);
}
return result;
}
public List<Integer> collectUsingLoopV2(List<Integer> ints){
List<Integer> result = new ArrayList<>();
for (int i : ints){
if ( f2.test(i) && f1.test(i) ) result.add(i);
}
return result;
}
Again will the Java stream in collectUsingStreams method after collect is called run f1 and f2 in the order described in collectUsingLoopV1 (f1 is evaluated before f2) or as described in collectUsingLoopV2 (f2 is evaluated before f1)?
And will that order change in future versions of Java or there is an official documentation that guarantees us the order of f1 and f2 calls?
Edit
Thanks for all the answers and comment but unfortunately I still dont see good explanation on the ordering of processing the elements. The docs do say that the encounter order will be preserved for lists but they dont specify how those elements will be processed. For example in case of findFirst the docs guarantees that map1 will first see 1 then 2 but it does not say that map1 wont be executed for 4 and 5. Does it mean that we cant guarantee that our order of processing will be as we expect the in fure versions of Java? Probably yes.