I mean the question in the sense of: Should the occurrence of simple loops on collections in code in Java 8 and higher be regarded as code smell (except in justified exceptions)?
When it came to Java 8, I assumed that it would be good to treat everything with Stream API now wherever possible. I thought, especially when I use parallelStream() wherever I know that order doesn't matter, this gives the JVM the ability to optimize the execution of my code.
Teammates think differently here. They think lambda transforms are hard to read and we don't use streams much now. I agree that streams are hard to read if the code formatter forces you to write them like this:
return projects.parallelStream().filter(lambda -> lambda.getGeneratorSource() != null)
.flatMap(lambda -> lambda.getFolders().prallelStream().map(mu -> Pair.of(mu, lambda.getGeneratorSource())))
.filter(lambda -> !lambda.getLeft().equals(lambda.getRight())).map(Pair::getLeft)
.filter(lambda -> lambda.getDerivative().isPresent() || lambda.getDpi().isPresent()
|| lambda.getImageScale().isPresent() || lambda.getImageSize().isPresent())
.findAny().isPresent();
I would rather prefer to write them like this:
return projects.parallelStream()
// skip all projects that don’t declare a generator source
.filter(λ ->
λ.getGeneratorSource() != null
)
/* We need to remove the folders which are the source folders of their
* project. To do so, we create pairs of each folder with the source
* folder … */
.flatMap(λ ->
λ.getFolders().stream()
.map(μ ->
Pair.of(μ, λ.getGeneratorSource()) // Pair<Folder, Folder>
)
)
// … and drop all folders that ARE the source folders
.filter(λ ->
!λ.getLeft().equals(λ.getRight())
)
/* For the further processing, we only need the folders, so we can unbox
* them now */
.map(Pair::getLeft)
// only look for folders that declare a method to generate images
.filter(λ ->
λ.getDerivative().isPresent() || λ.getDpi().isPresent() ||
λ.getImageScale().isPresent() || λ.getImageSize().isPresent()
)
// return whether there is any
.findAny().isPresent();
Yes, return is at the top, and it looks quite differently than:
for (Project project : projects) {
if (Objects.nonNull(project.getGeneratorSource())) {
continue;
}
for (Folder folder : project.getFolders()) {
if (folder.equals(project.getGeneratorSource())) {
continue;
} else if (folder.getDerivative().isPresent() || folder.getDpi().isPresent()
|| folder.getImageScale().isPresent() || folder.getImageSize().isPresent()) {
return true;
}
}
}
return false;
But isn’t that just a matter of habit? Therefore my question is more a question of assessment:
Should Stream be something basic to use (should Java beginners learn for loops at all/first, or should they first learn to use streams), or is that too much premature optimization, and one should use streams rather after it is shown that a code makes a bottleneck.
(Less opinion-based: How do modern Java teaching books (are there still books in IT training?) handle this?)