'Find' method for Iterable

Nir Lisker nlisker at gmail.com
Sat Sep 19 22:27:57 UTC 2020


>
> While it might not be difficult to add a find() method to Iterable, why
> limit it to
> the find operation, and what about all the other operations available on
> Stream?


Good question. I would say it's a matter of how much it is used and what
it takes to implement it. The find operation is a lot more common than
reduce from what I observe, for example, so I wouldn't suggest reduce to be
added..A map(Function<T,R>) operation would require creating a new
collection/iterable internally, and that can be messy (you could
preemptively create and pass your own, but then I doubt the worthiness of
it). forEach already exists. I just don't see anything enticing.

Maybe what's necessary is a way to convert an Iterable to a Stream.


Most Iterables are Collections and arrays, and these are easy to convert,
so I'm not sure if that really helps. Besides,the idea is to avoid Stream,
as I've mentioned, due to the cumbersomeness and the overhead of creating a
stream. If I need to do

iterable.stream().filter(person -> person.id == 123456).findAny/First()

then I didn't really solve my problem.

On the other hand, your examples use a list. The List interface already has
> methods
> indexOf/lastIndexOf which search the list for a particular object that's
> compared
> using equals(). It seems reasonable to consider similar methods that take a
> predicate instead of an object.


I could have used a Set just as well. As for indexOf(Predicate<T>), I would
say that it is useful (but personally, I hardly ever need the index of an
object, I need the object itself). Interestingly, removeIf(Predicate<T>)
exists, but remove(Predicate<T>) doesn't. I would think twice before
suggesting to add it though.

Ultimately, you have access to a lot of analytics and codebase scans. If
you know which patterns are used a lot more than others it would be a good
guide. If there are a lot of iterations in order to find an object, its
index, or to remove it (or something else), perhaps it's worth supplying
these methods. After all, forEach(Consumer<T>) exists and it iterates while
calling accept(t) - not that different from iterating with test(t).

P.S. lastIndexOf I find odd in the sense that it's the only method I found
that iterates backwards, We don't have, removeLast, removeIfBackwards,
forEachBackwards, a backwards for-each loop, or addLast (the latter is
add(list.size()-1, e); ).

- Nir

On Thu, Sep 17, 2020 at 1:32 AM Stuart Marks <stuart.marks at oracle.com>
wrote:

>
>
> On 9/16/20 1:59 PM, Remi Forax wrote:
> > ----- Mail original -----
> >> De: "Nir Lisker" <nlisker at gmail.com>
> >> À: "core-libs-dev" <core-libs-dev at openjdk.java.net>
> >> Envoyé: Lundi 14 Septembre 2020 20:56:27
> >> Objet: 'Find' method for Iterable
> >
> >> Hi,
> >>
> >> This has probably been brought up at some point. When we need to find an
> >> item in a collection based on its properties, we can either do it in a
> >> loop, testing each item, or in a stream with filter and findFirst/Any.
> >>
> >> I would think that a method in Iterable<T> be useful, along the lines
> of:
> >>
> >> public <T> Optional<T> find(Predicate<T> condition) {
> >>     Objects.requireNonNull(condition);
> >>     for (T t : this) {
> >>          if (condition.test(t)) {
> >>              return Optional.of(t);
> >>         }
> >>     }
> >>     return Optional.empty();
> >> }
> >>
> >> With usage:
> >>
> >> list.find(person -> person.id == 123456);
> >>
> >> There are a few issues with the method here such as t being null in
> >> null-friendly collections and the lack of bound generic types, but this
> >> example is just used to explain the intention.
> >>
> >> It will be an alternative to
> >>
> >> list.stream().filter(person -> person.id == 123456).findAny/First()
> >> (depending on if the collection is ordered or not)
> >>
> >> which doesn't create a stream, similar to Iterable#forEach vs
> >> Stream#forEach.
> >>
> >> Maybe with pattern matching this would become more appetizing.
> >
> > During the development of Java 8, we first tried to use
> Iterator/Iterable instead of using a novel interface Stream.
> > But a Stream cleanly separate the lazy side effect free API from the
> mutable one (Collection) and can be optimized better by the VM (it's a push
> API instead of being a pull API).
> >
> > The other question is why there is no method find() on Collection, i
> believe it's because while find() is ok for any DB API, find() is dangerous
> on a Collection because the execution time is linear, so people may use it
> instead of using a Map.
>
>
> Hi Nir,
>
> Rémi is correct to point out this distinction between the lazy operations
> (which
> appear on Stream) and the eager (and possibly mutating) operations on
> Collections. I
> think we want to preserve this distinction.
>
> While it might not be difficult to add a find() method to Iterable, why
> limit it to
> the find operation, and what about all the other operations available on
> Stream?
> Maybe what's necessary is a way to convert an Iterable to a Stream. In
> fact, this is
> already possible:
>
>      StreamSupport.stream(iterable.spliterator(), false)
>
> Well, this is mouthful, so maybe there ought to be an easier way to
> convert an
> Iterable to a Stream.
>
> On the other hand, your examples use a list. The List interface already
> has methods
> indexOf/lastIndexOf which search the list for a particular object that's
> compared
> using equals(). It seems reasonable to consider similar methods that take
> a
> predicate instead of an object.
>
> Does either of these sound promising?
>
> s'marks
>


More information about the core-libs-dev mailing list