JDK-8198620 : New Convenient-Methods for java.util.stream.Stream
  • Type: Enhancement
  • Component: core-libs
  • Sub-Component: java.util.stream
  • Affected Version: 10
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • Submitted: 2018-02-23
  • Updated: 2018-10-18
  • Resolved: 2018-05-26
Related Reports
Duplicate :  
Relates :  
Description
A DESCRIPTION OF THE REQUEST :
I really like the power of the stream API, but it would be helpful to have some convenient methods, to further reduce the length of code. All of them are only shortcuts for already existing functionality.

Given the following Example-Stream:

final Stream<Number> numbers = ... 

1. ofClass(<class>)

numbers.ofClass(Double.class). 
This would be the equivalent to 
numbers.filter(Double.class::isInstance).map(Double.class::cast)
Of course this could be reduced to one operation with a flatmap-function, but this would every project have to do and is still not as intuitiv. 

2. toList / toSet
numbers.toList()/toSet()
This would be the equivalent to
numbers.collect(Collectors.toList()/toSet())

Cheers,
Rainer

JUSTIFICATION :
Having written a lot of C#-Code where those methods are available it is not really intuitive and also quite long to write this functionality down. 

Especially the ofClass-method would be really handy since I have a ton of places in my applications where I have always those to methods (filter + map). 



Comments
About the downcasting, I'd like to bring another point of view - that of JEP 305: Pattern Matching [1]. It gives the example if (obj instanceof Integer) { int intValue = ((Integer) obj).intValue(); // use intValue } And says "It is tedious; doing both the type test and cast should be unnecessary (what else would you do after an instanceof test?)." (I ignored the deconstruction part of the above, which somewhat weakens my argument.) I think this argument can be applied to streams. If I do `.filter(x -> x instanceof Y)`, what else would I do if not `.map(x -> (Y) x)`? I view it as a compound operation, a filter for `instanceof` by itself doesn't really help. As for the solution, the obvious one is indeed a single "filterByInstanceofThenCast" method. However, I wonder if the solution of JEP 305 can be applicable for streams. Suppose this example from the JEP is valid: if (x matches Integer i) { // can use i here } Ideally, `.filter(x -> x matches Y)` would return Stream<Y> (again, ignore the unboxing because it will need to be boxed again for the stream, at least until Valhalla gives generics for primitives), but this breaks the current API, unless an overloading for `filter` can be done somehow. Any other way for `matches` to be used in a stream to return (a stream of) the matched type directly is comparable. [1] http://openjdk.java.net/jeps/305
18-10-2018

> The downcast operation occurs occasionally, but writing out the code oneself (or writing a utility method) isn't all that bad. The value provided by a putative ofClass(<class>) method is not as useful as it seems, as it only handles reifiable types, not parameterized types It might seem "not as useful" from the purist point of view, because it does not cover all possible cases. Still it's extremely useful in practice. People very often work with reifiable types. Especially this true for stream element types (stream of lists or maps are rare beasts). In our codebase I found at 49 uses of `.filter(Xyz.class::isInstance).map(Xyz.class::cast)`, 46 uses of `.filter(x -> x instanceof Xyz).map(x -> (Xyz)x)` and 140 uses of StreamEx select method (it is an extension to the Stream API which provides the same functionality). Many of StreamEx usages could be converted to standard Stream API were it contain similar method. Also four patterns like `.map(x -> ObjectUtils.tryCast(x, Xyz.class).filter(Objects::nonNull)` where tryCast is an utility method which returns null when cast cannot be performed. I believe some people achieve the same result using other code patterns as well. For me at least 239 usages of some pattern is the sign that it's quite useful. E.g. in comparison we have only 10 usages of three-arg Stream.reduce overload, and my practical experience tells me that filtering the stream by class is much more useful than three-arg reduce. Or we have 107 usages of "noneMatch" method, so the demand for filtering by class is at least comparable to the demand for "noneMatch" (and similarly to the filtering by class "noneMatch" could be expressed in the terms of other stream methods). Writing such code by hand is not very nice, because it's verbose and contains a repeating class reference, so you may occasionally introduce a bug during refactoring (e.g. erroneously add a new step between filter and map or change one of the class references and forgetting to change another one). An advice to write an utility method is also not very useful. Unless "chain" feature is implemented (see JDK-8140283) such utility method would break the fluent style like this: ofClass(ofClass(list.stream(), Xyz.class).map(...), Abc.class).collect(Collectors.toList()) This sample is quite short, but already completely unreadable. Also as usually without standard way to solve this in the big project people would need to know somehow which utility class should be used. The practice shows that people learn to use instance methods much quicker (because they are suggested by IDE code completion), compared to static methods in utility classes. Finally implementation of the proposed ofClass method can be a little more optimal, compared to the manual filter+map steps; @SuppressWarnings("unchecked") default <TT> Stream<TT> ofClass(Class<TT> clazz) { return (Stream<TT>) filter(clazz::isInstance); } Unchecked cast is actually safe here and you don't even need a map step if you already filter. This reduces pipeline depth by one step decreasing amount of garbage created. Such trick would be completely dirty in user code, but perfectly ok for library code.
26-05-2018

The downcast operation occurs occasionally, but writing out the code oneself (or writing a utility method) isn't all that bad. The value provided by a putative ofClass(<class>) method is not as useful as it seems, as it only handles reifiable types, not parameterized types. For example, such a method wouldn't help filtering and converting a Map<Object, String> to Map<String, String>. Accumulating the stream elements to a List is much more common than any other collection. The Stream.toList() request is covered by JDK-8180352. Closing as a duplicate.
26-05-2018