'Find' method for Iterable
Nir Lisker
nlisker at gmail.com
Tue Nov 17 09:46:43 UTC 2020
I think that the discussion is still at the level of if it's an acceptable
addition and not yet at where to implement it. I gave my reasoning in
response to Stuart's points as to what I think is acceptable and what
isn't. Whether the java.lang package has special status is a bit ahead of
where we are now.
On Wed, Nov 11, 2020 at 7:22 PM Justin Dekeyser <justin.dekeyser at gmail.com>
wrote:
> 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