The Difference between For-Each Loop and forEach()
People who have played enough with the brand new Java 8 will be familiar with the new forEach(fn(x)) loop in lambda. This syntax is quite popular among developers because it’s easier and prettier to use than the normal For-Each loop. It is also popular in other languages, especially JavaScript.
The problem is, I’ve seen a very specific bug, which is related to this “functional” forEach() loop, quite a few times, again and again. People tend to think that these two kinds of loop are 100% identical, which is not. Although Java 8’s type-safety make it harder for people to make this mistake because of its compilation error, it’s still not 100% safe.
A very wise guy told me once:
You are smart if you could know what is similar, but smarter people know what the differences are.
I really agree to this saying, and I always try to understand various stuffs so that I can know whether there are differences between the somewhat similar stuffs. This has been pretty useful in my life as a programmer and as a non-programmer, and I would suggest you to do the same.
Let’s just cut the chitchat and take a look at a code example:
List<Person> persons = getPersons();
// normal foreach
for (Person person : persons) {
person.setLastUpdate(System.currentTimeMillis());
}
// lambda
persons.forEach(person -> {
person.setLastUpdate(System.currentTimeMillis());
});
In this case, they are actually doing the same thing, and we don’t have any problem whatsoever, but let’s take a look at another example. Again, they are similar, but not identical.
public static void setProfileNormalForLoop() {
List<String> javaPropertyInSortedPriority = getPossiblePropertyKey();
for (String propertyKey : javaPropertyInSortedPriority) {
String propertyValue = System.getProperty(propertyKey);
if (propertyValue != null) {
Configuration.PROFILE = propertyValue;
System.out.println("Set profile to: " + propertyValue);
return;
}
}
}
Basically, it takes the first java property it found to set the profile of the application. Now, let’s convert this naively to forEach(fn(x)) with lambda:
public static void setProfileForEachLambda() {
List<String> javaPropertyInSortedPriority = getPossiblePropertyKey();
javaPropertyInSortedPriority.forEach(propertyKey -> {
String propertyValue = System.getProperty(propertyKey);
if (propertyValue != null) {
Configuration.PROFILE = propertyValue;
System.out.println("Set profile to: " + propertyValue);
return;
}
});
}
And call them together for a direct comparison:
public static List<String> getPossiblePropertyKey() {
List<String> keys = new ArrayList<>();
keys.add("profile");
keys.add("stage");
keys.add("oldprofilekey");
return keys;
}
public static void main(String[] args) {
System.setProperty("profile", "staging");
System.setProperty("stage", "quality");
System.setProperty("oldprofilekey", "live");
System.out.println("Using the normal for loop:");
setProfileNormalForLoop();
System.out.println("\nUsing the forEach() lambda:");
setProfileForEachLambda();
}
Running those blocks of codes will actually return you:
Using the normal for loop:
Set profile to: staging
Using the forEach() lambda:
Set profile to: staging
Set profile to: quality
Set profile to: live
As we could see, when we’re using the normal For-Each loop, the code could stop in the middle and just exit the whole function. However, with the forEach(fn(x)) loop, the return is actually inside an anonymous function, and the return will never return from the main function because it’s just returning from the anonymous function.
In Java, this kind of mistake is actually harder to make because if you put a break keyword inside the anonymous function, you will get a compile time error. Returning an object is also not possible because of Java’s type safety (the function was marked to return void). In JavaScript or other dynamic language, it’s possible to return an object, so if people just convert the code without thinking in those languages, it will return only undefined or null, because the return actually only exit from the anonymous function instead of the main function.
But seriously, this forEach() where we actually pass the function should be actually used for data processing and not for returning in the middle. If we want to do that, we have the old classic For-Each to do that. If we want to do this kind of stuff with Java 8’s functional stuff or other functional language, there’s usually the findFirst() or findAny() method. Anyone trying to program functionally should also think in terms of function.
That’s it. Hopefully you find it informative.
p.s. It’s nice that we could know that one 1000€ watch is actually only a branded 10€ watch, but it hurts more if we can’t differentiate between a 10€ watch and a 1000€ watch, and bought the 10€ watch for 1000€.