4 ms·
The functional style eliminates types of bugs that can happen with for loops and makes the code easier to read. For example, consider the task of removing name
by cpprototypes 11y ago
The functional style eliminates types of bugs that can happen with for loops and makes the code easier to read. For example, consider the task of removing names that start with "B" from a list. This is the for loop way:
List<String> names = Lists.newArrayList("Alice", "Tom", "Bob", "Brandon", "John", "James", "Ben");
List<String> bNames = new ArrayList<>();
for (String name : names) {
if (name.startsWith("B")) {
bNames.add(name);
}
}
names.removeAll(bNames);
And the functional way:
names = names.stream().filter(name -> !name.startsWith("B")).collect(Collectors.toList());
The basic problem with the for loop is that you need create a temporary list to do the work. It's low level coding that requires telling the computer exactly where to put the temporary results, then what to do with it. I've seen bugs where developers accidentally return the wrong list (such as bNames in this example) or later modify the wrong one. When the code becomes more complicated, there's often a lot of these temporary variables that greatly reduce code readability and allow subtle bugs to occur.
- newgame 11y agoWhat about iterating from the end of the list in this particular case. This way you don't need a temporary list for (int i = names.size()-1; i >= 0; i--) { String name = names.get(i); if (name.startsWith("B")) names.remove(i); } Admittedly, needing to use an explicit index counter is not as nice (and more prone to errors) as using the other for syntax. But one could imagine a language with e.g. macros that made the backwards-looping syntax more intuitive (I assume a single-threaded situation and an ArrayList). Your general point still stands though.
- wcummings 11y agoBoth implementations are incorrect as they needlessly mutate the input list: List<String> namesNotStartingWithB = new ArrayList<>(); for (String name : names) { if (!name.startsWith("B")) { namesNotStartingWithB.add(name); } } Imagine a scenario where you're filtering one of the arguments to a method: the input will be mutated with no indication to the caller (or in the method signature), causing bugs, iterator invalidation etc.
- newgame 11y agoGood additional point that demands repetition: Don't mutate arguments to a (public) method if not absolutely necessary. Just to be clear, that does not make the approaches "incorrect". The list "names" is not necessarily an argument to a method. It might be a local, intermediate result that does not have the risk of "mutation at a distance".
- kristianp 11y agoI know what you're trying to say, but the problem definition is to mutate the list, removing items that start with B.
- elFarto 11y agoHow about: Iterator<String> it = names.iterator(); while (it.hasNext()) { if (it.next().startsWith("B")) { it.remove(); } }
- newgame 11y agoYes, much better. For anyone interested, here's a link with background information: http://stackoverflow.com/questions/1196586/calling-remove-in-foreach-loop-in-java http://stackoverflow.com/questions/1196586/calling-remove-in...