Guava: Stream replacement for FluentIterable.filter(Class)

Created on 13 Jan 2017  Â·  4Comments  Â·  Source: google/guava

One of the things I miss most when switching from FluentIterable to streams is a filter method that takes a Class<T> argument and returns a Stream<T>. Maybe Guava could provide a utility for this?

Doing it manually like stream.filter(o -> o instanceof MyClass).map(o -> (MyClass)o) involves having to specify MyClass twice and is not safe against accidental modifications of only the latter operation. Furthermore I prefer to avoid manual casts where possible because someone reading the code would always need to ask themselves whether the cast is actually safe.

I see two ways a utility method could be done:

  • A method <T> Stream<T> filter(Stream<? extends T> stream, Class<T> cls). Unfortunately this breaks the fluentness.
  • A method <T> Function<? extends T, Stream<T>> filter(Class<T> cls) which could be used with Stream.flatMap. Unfortunately, this adds extra object allocations.

I am not sure which of these would be better, partially because I am unsure what kind of guarantees and optimizations might be lost when using the flatMap approach (given that using flapMap makes it impossible to use any information about the size of the input stream, whereas with a combination of map and filter it is still knowable that the resulting size is at most the size of the input stream). This is one of the reason why I think it would be beneficial for Guava to add it instead of just implementing it myself.

package=collect status=will-not-fix type=addition

Most helpful comment

@PhilippWendler , you can also use

stream.filter(MyClass.class::isInstance).map(MyClass.class::cast)

I admit it still duplicates MyClass but at least is less explicit. I usually format it on single line even if every element of fluent chain is otherwise on its own line.

All 4 comments

So far, when this has come up for our internal users, we've suggested
either .filter().map(), or just .filter() and deal with slapping an
unchecked cast somewhere to make it all work. And so far, this has
seemed... mostly acceptable. I intend to reevaluate after a few more months
of usage builds up.

Also, one vague concern I've always had with the existing filter(Class)
methods is that some fraction of their users probably don't even mean to
filter, but rather fully expect all the elements to already be of that
type. The fact that any unexpected objects are silently discarded then
saddens me, so one good thing about recommending .filter().map() is that in
this circumstance you can just drop the .filter(). But I haven't actually
dug into how common this circumstance is.

On Fri, Jan 13, 2017 at 10:48 AM, Philipp Wendler notifications@github.com
wrote:

One of the things I miss most when switching from FluentIterable to
streams is a filter method that takes a Class argument and returns a
Stream. Maybe Guava could provide a utility for this?

Doing it manually like stream.filter(o -> o instanceof MyClass).map(o ->
(MyClass)o) involves having to specify MyClass twice and is not safe
against accidental modifications of only the latter operation. Furthermore
I prefer to avoid manual casts where possible because someone reading the
code would always need to ask themselves whether the cast is actually safe.

I see two ways a utility method could be done:

  • A method Stream filter(Stream stream, Class
    cls). Unfortunately this breaks the fluentness.
  • A method Function> filter(Class cls)
    which could be used with Stream.flatMap. Unfortunately, this adds
    extra object allocations.

I am not sure which of these would be better, partially because I am
unsure what kind of guarantees and optimizations might be lost when using
the flatMap approach (given that using flapMap makes it impossible to use
any information about the size of the input stream, whereas with a
combination of map and filter it is still knowable that the resulting
size is at most the size of the input stream). This is one of the reason
why I think it would be beneficial for Guava to add it instead of just
implementing it myself.

—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/google/guava/issues/2710, or mute the thread
https://github.com/notifications/unsubscribe-auth/AA5ClzP1qfmFYt6oXFqaGbxV2YPR3AgHks5rR8b1gaJpZM4LjL1v
.

--
Kevin Bourrillion | Java Librarian | Google, Inc. | [email protected]

@PhilippWendler , you can also use

stream.filter(MyClass.class::isInstance).map(MyClass.class::cast)

I admit it still duplicates MyClass but at least is less explicit. I usually format it on single line even if every element of fluent chain is otherwise on its own line.

An other approach is to define the function

public static <S, T extends S> Function<S, Stream<T>> subclasses(Class<T> clazz) {clazz) {
    return x -> clazz.isInstance(x) ? Stream.of(clazz.cast(x)) : Stream.empty();
}

and then use stream.flatMap(subclasses(MyClass.class).

Yeah, though Philipp mentioned that this approach causes a lot of garbage
allocation. I'd be reluctant to add this method to Guava because I just
can't see .filter().map() as bad enough to justify that.

But, if anyone reading wants to add this to their own codebase for their
own use, note that there's no need an <S> parameter; just use a return
type of Function<Object, Stream<T>> and you're set.

On Fri, Feb 10, 2017 at 11:48 PM, Simon Legner notifications@github.com
wrote:

An other approach is to define the function

public static Function> subclasses(Class clazz) {clazz) {
return x -> clazz.isInstance(x) ? Stream.of(clazz.cast(x)) : Stream.empty();
}

and then use stream.flatMap(subclasses(MyClass.class).

—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/google/guava/issues/2710#issuecomment-279128259, or mute
the thread
https://github.com/notifications/unsubscribe-auth/AA5Cl0B6_APpB7ki5eL7Xk-WC5AKbNxyks5rbWfSgaJpZM4LjL1v
.

--
Kevin Bourrillion | Java Librarian | Google, Inc. | [email protected]

Was this page helpful?
0 / 5 - 0 ratings

Related issues

gissuebot picture gissuebot  Â·  5Comments

cowwoc picture cowwoc  Â·  3Comments

StuAtGit picture StuAtGit  Â·  5Comments

cpovirk picture cpovirk  Â·  5Comments

gissuebot picture gissuebot  Â·  3Comments