Please add Stream.filterByType(Class) and possibly Stream.forEachOfType(Class, Consumer)
Paul Sandoz
paul.sandoz at oracle.com
Wed May 1 13:21:18 PDT 2013
Hi Derek,
Have you tried using flatMap? For example:
Function<Item, Stream<Type>> f = i -> {
return (i instance of Type) ? Stream.of((Type) i) : Stream.empty();
};
items.stream().flatMap(f)...
You could easily create your own method that returns the Function used for flatMap:
items.stream().flatMap(SomeClass.byType(Thing.class))...
Note that we have optimized the Stream.of method so it is not as inefficient as one might initially expect.
Hth,
Paul.
On May 1, 2013, at 9:38 PM, Derek Foster <vapor1 at teleport.com> wrote:
> Hi, folks!
>
> I've been modifying some of my old projects to use one of the recent Lambda-enabled JDKs. One issue that I have run across is code that handles heterogenous collections. Typically, I want to iterate over the collection, doing something with only the items within the collection that are subtypes of a specific type. In Java 7, this usually requires both an instanceof test and a cast, like this:
>
> class Thing extends Item { ... }
>
> List<Thing> getThings(List<Item> items) {
> List<Thing> things = new ArrayList<>();
> for (Item item : items) {
> if (item instanceof Thing) {
> things.add((Thing)item);
> }
> }
> return things;
> }
>
> With the current implementation of streams, I currently have to rewrite this as something like:
>
> List<Thing> getThings(List<Item> items) {
> return items.stream()
> .filter(item -> item instanceof Thing)
> .map(item -> (Thing)item)
> .collect(Collections.toList());
> }
>
> This works, but it seems inelegant to have to 'stutter' by repeating the type that I am converting to twice (in an instanceof and in a cast). It also introduces the possibility for error if someone modifies one of these without modifying the other. Since the instanceof and the cast are so tightly linked, I would prefer to apply the "Don't Repeat Yourself" principle, and write this as:
>
> List<Thing> getThings(List<Item> items) {
> return items.stream()
> .filterByType(Thing.class)
> .collect(Collections.toList()).
> }
>
> This could be easily implemented in the JDK by adding a method to Stream such as:
>
> default <S> Stream<S> filterByType(Class<S> type) {
> return filter(item -> type.isInstance(item))
> .map(item -> type.cast(item));
> }
>
> although better-performing implementations (that don't create an intermediate Stream object) are certainly possible.
>
> I think that having such a method in Stream would often be useful. For instance, this kind of heterogenous collection shows up quite a bit in various "list of listeners" implementations, where a method performing a listener callback must iterate a list of listeners, looking for a listener of a type which can handle the callback in question, and then invoke the callback on that listener:
>
> void fireNiftyEvent(NiftyEvent event) {
> this.listeners.stream()
> .filterByType(NiftyEventListener.class)
> .forEach(listener -> listener.handleNiftyEvent(event));
> }
>
> Internally, a 'filterByType' followed by a 'forEach' could be optimized (to a forEachOfType(Class, Consumer)) to make the iteration more efficient. It might even be desirable to have a forEachOfType method exposed as a public method on Stream, to make the above example even shorter:
>
> void fireNiftyEvent(NiftyEvent event) {
> this.listeners.stream().forEachOfType(NiftyEventListener.class, listener -> listener.handleNiftyEvent(event));
> }
>
> Anyway, what would I need to do to encourage the addition of either or both of these methods to the Stream class?
>
More information about the lambda-libs-spec-observers
mailing list