'Find' method for Iterable
Michael Kuhlmann
jdk at fiolino.de
Mon Sep 21 08:08:27 UTC 2020
Hi Nir,
at first I thought "Wow, it would be really cool to have that method in
Iterable! Why isn't it there already?"
But after thinking about it, I'm now convinced that it would be a bad
idea. Because it extends the scope of this small, tiny Iterable
interface to something bigger which it shouldn't be.
When some class implements Iterable, it just says "you can iterate over
my something which I call elements". Nothing more. Now when Iterable
implements find() by default, then automatically all classes which just
want to enable users to iterate over elements also tell them that there
can be something useful found in these elements, which is not
necessarily the case.
For example, BitSet could immplements Iterable<Integer>. That doesn't
make much practical sense, but from the definition of a BitSet it's
understandable: A BitSet can be seen as a set of integer values, why
shouldn't someone iterate over them. But now, when you add find() to
Iterable, it immediately tells users: Hey, you can find integers in me,
and when you found one, you get it returned. Which is beyond the use
case of a BitSet.
forEach() is different, because forEach just iterates over the elements
and nothing more, which is in the scope of an Iterable.
A second argument against adding find() is that such a generic method
could conflict with more specialized methods in subinterfaces or
classes. I like the idea of having indexOf(Predicate<T>) in List
interface, but having both of them would be redundant.
And a third argument is that it can break existing code. An implementor
of Iterable could already define a find() method, but return the index
of the element instead of the element itself, or throw some checked
exception. This code wouldn't compile any more.
So while the idea of having find() in Iterable is great, the arguments
against are heavier from my point of view.
-Michael
On 9/16/20 11:36 PM, Nir Lisker wrote:
> I don't see a reason to put it Collection when it extends Iterable anyway,
> and the method just requires iteration. As for execution time, true, it's
> faster, but Map uses a lot more memory, so it's a tradeoff. For smaller
> lists, linear time is acceptable. Currently I'm using Maps actually, but I
> find that when there are many small maps, having many small lists is better
> for memory and the search time is similar. Additionally, a Map works only
> for searching by 1 key, but with a Collection/Iterable I can search by any
> property, and we're not about to use a Map for every property. So, overall,
> I don't think Map is a competitor in this market. It's also possible to
> specify that the complexity is linear in an @implNote to avoid surprises.
>
> - Nir
>
> On Wed, Sep 16, 2020 at 11:59 PM Remi Forax <forax at univ-mlv.fr> 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.
>>
>>>
>>> - Nir
>>
>> Rémi
>>
More information about the core-libs-dev
mailing list