Java 8 Stream API allows programmers to express their thoughts in much less code than before. However it appears that even using Stream API some developers write longer than necessary. Sometimes this makes code not only more confusing and hard to understand, but also less performant. Often it’s unclear why people do so. Probably they have read only a small part of documentation and are not aware of other Stream API features. Or probably they did not read any documentation at all, just saw some sample and wanted to do something similar. Sometimes the code resembles an old joke about the problem reduced to the already solved one.

Here I collected the code samples which I encountered in practice. Hopefully this post will help people writing code which will be a little more beautiful and run a little faster. Good IDE can warn you about many of these things, but remember that no IDE will replace your head.

1. Stream created from collection without intermediate operations is usually redundant

If you have no operations like map or filter , usually you don't need a stream.

1.1. collection.stream().forEach()

Want to do something for every collection element? Great. Why do you need a stream for this? Write simply collection.forEach() . In most of the cases it’s the same, but shorter and produces less garbage. Some people fear that there’s some difference in functionality, but cannot explain it. I’ve heard something like “forEach does not guarantee an order”. Yes, the stream forEach does not guarantee it (by specification, practically it's ordered if not parallelized), but collection forEach guarantees it for ordered collections. If you use stream.forEach() , then you don't care about order, so you will be fine if the order guarantee appears. I know only one difference in Java standard library: synchronized collections, created via Collections.synchronizedXyz() . In this case collection.forEach() synchronizes the whole operation while collection.stream().forEach() does not synchronize anything. If you are using the synchornized collection, you likely want the synchronization, so removing stream() will make things better.

1.2. collection.stream().collect(Collectors.toList())

Want to transform some collection into a list? Fine. You can do it since Java 1.2: new ArrayList<>(collection) (okay, okay, there were no generics before Java 5 and no diamond operator before Java 7). This is not only shorter, but also faster and, again, produces less garbage. Sometimes much less, as new ArrayList will allocate an array of proper size in advance, while stream will add elements one-by-one resizing the array when necessary. Similarly, instead of stream().collect(toSet()) use new HashSet<>() , and stream().collect(toCollection(TreeSet::new)) should be replaced with new TreeSet<>() .

1.3. collection.stream().toArray(String[]::new)

A fancy new way of transforming a collection into an array is not better than good old collection.toArray(new String[0]) . Again, here you introduce less abstractions, thus this conversion will likely be more efficient. At least you don't need a Stream object.

1.4. collection.stream().max(Comparator.naturalOrder()).get()

There’s nice method Collections.max , but unfortunately many people forget about it for some reason. A Collections.max(collection) call will do the same, but producing less garbage. If you have your own comparator, use Collections.max(collection, comparator) . The Collections.max() method may not be suitable if you want to handle empty collection in some different way: a collection.stream().max(comparator).orElse(null) call chain looks better than collection.isEmpty() ? null : Collections.max(collection, comparator) .

1.5. collection.stream().count()

This is just bad: there is collection.size() ! In Java 9 count() will work quite fast, but in Java 8 this call always enumerates the whole collection even if the size is obviously known. Don't do this.

1.6. collection.stream.anyMatch(foo::equals)