'Find' method for Iterable

Justin Dekeyser justin.dekeyser at gmail.com
Wed Nov 11 17:22:10 UTC 2020


Hello all,

Were those downsides seriously considered and strongly argued against? :

(1) Iterable belongs to java.lang so, at least from how it is usually
approached, it's a feature at the very level of the language, not a
util feature like Collection. Adding a find on it, is it really
relevant, or should it be concerned with the simple idea of unlocking
the enhanced-for loop syntax?

(2) For the find() method to stop on any Iterable, the Iterable should
have finite size. This mean the size() method can be implemented too,
by this move:
```java
default int size() {
   int[] dummy = { 0 };
   Predicate<T> falsy = $ -> { dummy[0] += 1; return false; }
   find(falsy);
   return dummy[0];
}
```
At the very moment an Iterable<T> also has a size(), a Collection<T>
implementation can be provided (since the altering methods add/remove
are optional in this spec). So , long story short:

Adding a find() method on Iterable<T> with a contract that it stops
(stops, and not just linear search time), makes it a Collection. It
really looks like a nonsense. Or maybe you're going to relax the "this
method is guaranteed to stop" from the spec? Is it then still
relevant?

Note: This (2) problem is not a problem for now, because all the utils
that turns Iterable<T> to a collection like stuff, or a Stream, are
actually features *on those latter* classes. Hence it's not a concern
of Iterable<T> itself.
The problem with (1) is really a communication problem: nothing is
made in the Java documentation to clearly indicate whether or not
Iterable should be used businesswise, or is it only for
syntax-enhancement. (The same problem goes for AutoCloseable: you'll
really find two schools out of there.)

(3) To my opinion, most people are using Iterable either from a
collection (where the .stream().filter(p).findAny() makes the job.
It's not perfect but it's quite clear and flexible) or with a
ResultSet, where the SQL procedure could/should be used anyway. The
other cases look so exotic (but I might be wrong) that I really wonder
how a find method on a Iterable (and not Collection) stuff can be felt
so useful and language fundamental, that it should be in the java.lang
package.

Best regards,

Justin Dekeyser

On Tue, Nov 10, 2020 at 7:02 PM Nir Lisker <nlisker at gmail.com> wrote:
>
> Did this discussion get lost?
>
> On Sun, Sep 20, 2020 at 1:27 AM Nir Lisker <nlisker at gmail.com> wrote:
>
> > 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