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