It feels like java 8 streams and mapping functions are so verbose they aren't really an improvement. For example, I wrote some code that uses a collection to generate another, modified collection:
private List<DartField> getDartFields(Class<?> model) { List<DartField> fields = new ArrayList<>(); for (Field field : model.getDeclaredFields()) { if (!Modifier.isStatic(field.getModifiers())) { fields.add(DartField.getDartField(field)); } } return fields; }
This seems like the ideal use case for java 8 streams and their functions, so I rewrote it like that:
private List<DartField> getDartFields(Class<?> model) { return Arrays.asList(model.getDeclaredFields()) .stream() .filter(field -> !Modifier.isStatic(field.getModifiers())) .map(field -> DartField.getDartField(field)) .collect(Collectors.toList()); }
But I'm not sure I like that more. It's 236 characters as compared to 239 in normal-style java. It doesn't seem more or less readable. It's nice that you don't have to declare an ArrayList
, but needing to call .collect(Collectors.toList())
and Arrays.asList
(depending on the data type) isn't any better.
Is there some practical improvement to using .stream()
like this that I just don't get, or is this just a fun way to throw any coworkers for a loop who don't know functional programming?
I suppose if I were dynamically passing around filter or map lambdas it would be useful, but if you don't need to do that ...
Until we realized just how much performance can suffer from overusing them. To be clear, streams introduced in Java 8 were slow and the comparison from the title started to arise in many forms. Still the advantages were clear and once Java 11 came, streams were greatly optimized.
The Java 8 Streams API is fully based on the 'process only on demand' strategy and hence supports laziness. In the Java 8 Streams API, the intermediate operations are lazy and their internal processing model is optimised to make it being capable of processing the large amount of data with high performance.
There are a lot of benefits to using streams in Java, such as the ability to write functions at a more abstract level which can reduce code bugs, compact functions into fewer and more readable lines of code, and the ease they offer for parallelization.
The problem is that you are not using the Stream
API consistently. You are restricting the use case to something which can be best described as “actually not using the Stream
API” as you are insisting on returning a Collection
. That’s especially absurd as it’s a private
method so you are entirely able to adapt the callers as well.
Consider to change the method to
private Stream<DartField> getDartFields(Class<?> model) { return Stream.of(model.getDeclaredFields()) .filter(field -> !Modifier.isStatic(field.getModifiers())) .map(field -> DartField.getDartField(field)); }
and look what the caller(s) actually want to do. Usually they don’t need a Collection
as an end in itself, but want to perform an action or even more operations which could be chained, e.g. print them:
getDartFields(Foo.class).forEach(System.out::println);
The most interesting feature is the lazy nature of the stream, which implies that upon getDartFields
return, no action has been performed yet and if you use operations like findFirst
, there is no need to process all elements. You’ll lose this feature if you return a Collection
containing all elements.
This also applies to multi-step processing where processing ordinary lists implies that for each step a new list has to be created and populated with results.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With