Re: 'Find' method for Iterable
- Mail original - > De: "Brian Goetz" > À: "Michael Kuhlmann" , "core-libs-dev" > > Envoyé: Mardi 17 Novembre 2020 20:44:17 > Objet: Re: 'Find' method for Iterable > On 9/21/2020 4:08 AM, Michael Kuhlmann wrote: >> 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. > > This response captures the essence of the problem. You may think you > are asking for "just one more method on Iterable", but really, what you > are asking for is to turn Iterable into something it is not. Iterable > exists not to be "smallest collection", but as the interface to the > foreach-loop. Yes, we could have chosen a different design center for > Iterable (Eclipse Collections did, see RichIterable), but we didn't. > Adding this method merely sends the signal that we want to extend > Iterable to be more than it is, which opens the floodgate to requests > (and eventually demands) for more methods. > >> I can ask about Iterable#forEach - is it there only because it was there to >> begin with? Would it have been a bad idea to add one if we had streams >> already? > > While you can make an argument that forEach is excessive, the fact that > you make this argument illustrates the essential problem with this > proposal. Your argument wrt forEach is "If that makes sense, then so > does find." If you pull on that string, then this method forms the > basis of tomorrow's assumption: "You have forEach and find, it is > unreasonable to not add ". There is a good reason to have forEach, Iterable in the interface of things that can be iterated, and in Java, there are two ways to do an iteration, the internal iteration, where you ask the class to do the iteration for you using forEach and the external iterator, where you ask for an Iterator and use it to iterate outside of the class. In term of API design, it is push vs pull, using forEach, the element are is pushed to the Consumer while using an Iterator.next() allow you to pull the element. Usually, forEach() is faster but it takes a Consumer, so you are limited by the limitation of the lambda (you can not mutate the local variables of the enclosing scope). Using an Iterator is also more powerful because you can compose them. > > So, Iterable should stay simple. (The other arguments against it on > this thread, are also valid, but this is the most important one.) Yep. Rémi
Re: 'Find' method for Iterable
On 9/21/2020 4:08 AM, Michael Kuhlmann wrote: 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. This response captures the essence of the problem. You may think you are asking for "just one more method on Iterable", but really, what you are asking for is to turn Iterable into something it is not. Iterable exists not to be "smallest collection", but as the interface to the foreach-loop. Yes, we could have chosen a different design center for Iterable (Eclipse Collections did, see RichIterable), but we didn't. Adding this method merely sends the signal that we want to extend Iterable to be more than it is, which opens the floodgate to requests (and eventually demands) for more methods. I can ask about Iterable#forEach - is it there only because it was there to begin with? Would it have been a bad idea to add one if we had streams already? While you can make an argument that forEach is excessive, the fact that you make this argument illustrates the essential problem with this proposal. Your argument wrt forEach is "If that makes sense, then so does find." If you pull on that string, then this method forms the basis of tomorrow's assumption: "You have forEach and find, it is unreasonable to not add ". So, Iterable should stay simple. (The other arguments against it on this thread, are also valid, but this is the most important one.)
Re: 'Find' method for Iterable
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 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 falsy = $ -> { dummy[0] += 1; return false; } >find(falsy); >return dummy[0]; > } > ``` > At the very moment an Iterable also has a size(), a Collection > 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 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 to a collection like stuff, or a Stream, are > actually features *on those latter* classes. Hence it's not a concern > of Iterable 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 wrote: > > > > Did this discussion get lost? > > > > On Sun, Sep 20, 2020 at 1:27 AM Nir Lisker 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) 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. > > > > > > >
Re: 'Find' method for Iterable
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 falsy = $ -> { dummy[0] += 1; return false; } find(falsy); return dummy[0]; } ``` At the very moment an Iterable also has a size(), a Collection 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 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 to a collection like stuff, or a Stream, are actually features *on those latter* classes. Hence it's not a concern of Iterable 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 wrote: > > Did this discussion get lost? > > On Sun, Sep 20, 2020 at 1:27 AM Nir Lisker 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) 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), 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) exists, but remove(Predicate) 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) exists and it iterates while > > calling accept(t) - not that different from iterating with test(t).
Re: 'Find' method for Iterable
Did this discussion get lost? On Sun, Sep 20, 2020 at 1:27 AM Nir Lisker 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) 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), 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) exists, but remove(Predicate) 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) 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 > wrote: > >> >> >> On 9/16/20 1:59 PM, Remi Forax wrote: >> > - Mail original - >> >> De: "Nir Lisker" >> >> À: "core-libs-dev" >> >> 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 be useful, along the lines >> of: >> >> >> >> public Optional find(Predicate 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. >&
Re: 'Find' method for Iterable
I'm also thinking about something: If `T find()` gets implemented on `Iterable` with the contract that this method always ends, this implies that the amount of elements available in the Iterable is finite, which makes it possible to define a `int size()` by providing a trivial predicate. This would in fact turn `Iterable` to a redundant interface with `Collection`, as the only operations a collection could supply to enrich Iterable are just the add/remove operations, which are all optional by contract. This would sound a bit weird. Iterable will just turn to a readonly collection, and I think this was not the purpose (from a previous long post there were in this newsletter, about splitting the List interface in two interfaces, readonly and write-only). Regards, Justin Dekeyser On Mon, Sep 21, 2020 at 10:31 AM Justin Dekeyser wrote: > > Hi all, > > Correct me if I'm wrong, but isn't the goal of Iterable be used as > argument in a enhanced for-loop, that is: just allow a enhanced > syntax. > (Similarly, AUtoCloseable is what allows the try-with-resource syntax; > Throwable is what allows the throw syntax) > > As such, isn't it out of scope of Iterable to implement this famous find > method? > Since it's scope should just be to allow enhanced for-loop ? > > It's more like a general question in the Java programming language > design actually: is one expected to code with those interfaces in a > business-way, > or implement them to allow a specific syntax ? > > Regards, > > Justin Dekeyser > > On Mon, Sep 21, 2020 at 10:08 AM Michael Kuhlmann wrote: > > > > 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. 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) 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 > >
Re: 'Find' method for Iterable
Hi all, Correct me if I'm wrong, but isn't the goal of Iterable be used as argument in a enhanced for-loop, that is: just allow a enhanced syntax. (Similarly, AUtoCloseable is what allows the try-with-resource syntax; Throwable is what allows the throw syntax) As such, isn't it out of scope of Iterable to implement this famous find method? Since it's scope should just be to allow enhanced for-loop ? It's more like a general question in the Java programming language design actually: is one expected to code with those interfaces in a business-way, or implement them to allow a specific syntax ? Regards, Justin Dekeyser On Mon, Sep 21, 2020 at 10:08 AM Michael Kuhlmann wrote: > > 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. 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) 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 wrote: > > > >> - Mail original - > >>> De: "Nir Lisker" > >>> À: "core-libs-dev" > >>> 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 be useful, along the lines of: > >>> > >>> public Optional find(Predicate 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 ==
Re: 'Find' method for Iterable
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. 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) 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 wrote: - Mail original - De: "Nir Lisker" À: "core-libs-dev" 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 be useful, along the lines of: public Optional find(Predicate 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
Re: 'Find' method for Iterable
> > 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) 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), 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) exists, but remove(Predicate) 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) 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 wrote: > > > On 9/16/20 1:59 PM, Remi Forax wrote: > > - Mail original - > >> De: "Nir Lisker" > >> À: "core-libs-dev" > >> 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 be useful, along the lines > of: > >> > >> public Optional find(Predicate 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
Re: 'Find' method for Iterable
> > 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). No doubt, but a stream also creates overhead and is more cumbersome to use in the example I gave. Of course, I can create a helper static method `find(Collection, Predicate)`, but it's not much cleaner. (And the implementation can still be through Stream or Iterable,). I can ask about Iterable#forEach - is it there only because it was there to begin with? Would it have been a bad idea to add one if we had streams already? 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. 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 wrote: > - Mail original - > > De: "Nir Lisker" > > À: "core-libs-dev" > > 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 be useful, along the lines of: > > > > public Optional find(Predicate 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 >
Re: 'Find' method for Iterable
On 9/16/20 1:59 PM, Remi Forax wrote: - Mail original - De: "Nir Lisker" À: "core-libs-dev" 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 be useful, along the lines of: public Optional find(Predicate 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
Re: 'Find' method for Iterable
- Mail original - > De: "Nir Lisker" > À: "core-libs-dev" > 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 be useful, along the lines of: > > public Optional find(Predicate 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
'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 be useful, along the lines of: public Optional find(Predicate 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. - Nir