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 <fo...@univ-mlv.fr> wrote:

----- Mail original -----
De: "Nir Lisker" <nlis...@gmail.com>
À: "core-libs-dev" <core-libs-dev@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

Reply via email to