Add convenience collect methods to the Stream interface
Hi, I have raised an enhancement request (Incident Report 913453) about adding some convenience methods to the Stream interface that collect the stream and Pallavi Sonal asked me to start a thread here about that. More than 50% of our Stream collect calls use Collectors.toList() or Collectors.toSet() as arguments so I think it would be very handy if the Stream interface had default collectToList and collectToList and collectToMap methods. The advantages are: it would be easier to use code completion in IDEs. There are lot of classes starting with Collect so finding the Collectors class is a bit of a pain. one less method call in what is usually a long chain of calls. Regards, Rob Griffin Software Analyst, Spotlight on SQL Server Quest | R&D rob.griffin@quest.com
Just wanted to put my hand up to say that I have had the same experience, more than 50% of the time that I've used JDK8 streams I've used .collect(Collectors.toList()). But, as a counter point, let me speculate that many or most uses of streams fall into two broad use cases. One is for actual stream processing, where you aren't necessarily dealing with a stream that is materialized in memory, where you may want the power of parallel processing, where you certainly do want multiple stages of the stream processing chained together rather than having each stage materialize a new collection in memory, and this is what the stream API was created for. In this case, collecting to a list is common but not that common, perhaps not common enough to warrant a convenience method on Stream. The other broad use case category is to work with Lists and Sets in a functional programming style. In this case, you simply want to map, filter, flatMap etc every element in your list/set, and this is where collecting to a list and set is almost always the terminal operation for a stream. Although you can use the stream API to do this, I speculate that it wasn't primarily designed for this, and I think if the JDK wanted to provide first class support for doing this, there would be better ways to go about it - for example, by placing map/filter/flatMap methods directly on the Collection interfaces and returning the collections directly, rather than having to convert to a stream and back. Going through a stream still has its advantages (eg, it uses less memory since you don't need to materialize a new collection between each stage of the processing), but lacks the convenience that other collection APIs that offer true functional programming for Java have (eg, vavr, formerly javaslang). And, if the JDK was to start adding features for functional programming like that to its collection API, then it may be worth considering other features, such as persistent collections, that is, collections where operations such as add/remove aren't destructive, ie, immutable collections that support efficient (constant time) append/prepend operations that return a copy of the list. My point is that if the JDK does start to consider adding functional programming to its collection APIs, a holistic approach should be taken to work out what things would look like long term, even if not all of those features are added immediately (or ever). On Mon, 10 Dec 2018 at 10:04, Rob Griffin (rgriffin) <Rob.Griffin@quest.com> wrote:
Hi,
I have raised an enhancement request (Incident Report 913453) about adding some convenience methods to the Stream interface that collect the stream and Pallavi Sonal asked me to start a thread here about that.
More than 50% of our Stream collect calls use Collectors.toList() or Collectors.toSet() as arguments so I think it would be very handy if the Stream interface had default collectToList and collectToList and collectToMap methods.
The advantages are: it would be easier to use code completion in IDEs. There are lot of classes starting with Collect so finding the Collectors class is a bit of a pain. one less method call in what is usually a long chain of calls.
Regards,
Rob Griffin Software Analyst, Spotlight on SQL Server Quest | R&D rob.griffin@quest.com
-- James Roper Architect, Office of the CTO, Lightbend, Inc. @jroper <https://twitter.com/jroper> <https://www.lightbend.com/>
As will surprise no one, this was extensively discussed during the development of the Streams API. (Our default position on "convenience methods" is hostile. While everyone sees the benefit of convenience methods (it's convenient!), most people don't see the cost, which includes the complexity for users to understand the model by looking at the API; having lots of ad-hoc convenience method obscures the underlying model, making it harder for everyone to learn or reason about. That default position seems to stand up pretty well here, as the stream API is pretty well factored.) Let's be honest: the "convenience" or concision of being able to say .toList() instead of .collect(toList()) is really small. I don't think you'll be able to justify it by saying "but we do it a lot." (Digression: to whoever is about to say "then why `toArray()`? Arrays are different; for better or worse, they're part of the language, and they lend themselves particularly poorly to the Collector API, and there are particular parallelization optimizations that are possible for arrays that can't be accessed through Collector. End digression.) It is possible, however, that one could justify `toList()` on the basis of _discoverability_. (Though I'm having a hard time seeing any world where `toSet()` makes the cut.) New users who approach streams will not easily be able to figure out how to materialize a list from a stream, even though this is an entirely reasonable and quite common thing to want to do. Having to learn about `collect()` first is asking a lot of users who are still wrapping their heads around streams. Not only would `toList()` be more discoverable, it would provide a path to discovery of the rest of the `collect()` API. This is a point in its favor. A significant downside of adding `toList()` is that by diluting the orthogonality of the existing API, it provides both incentive and justification for further dilution, leading to someplace we don't want to be. (And, the cost of that falls heavily on the stewards, which in turn takes time away from far more valuable activities.) Put it this way: imagine we have a budget of one convenience method in Stream for every five years. Is this the one we want to spend the next five year's budget on? (And, who of the proponents will volunteer to answer the next 200 "I have an idea for a stream method" mails, explaining that the budget is spent?) So, summary: - I won't outright veto `toList`, as I would for almost all other "convenience" streams additions, because this one actually has a valid non-convenience argument; - But, it's still not a slam dunk. On 12/9/2018 5:44 PM, Rob Griffin (rgriffin) wrote:
Hi,
I have raised an enhancement request (Incident Report 913453) about adding some convenience methods to the Stream interface that collect the stream and Pallavi Sonal asked me to start a thread here about that.
More than 50% of our Stream collect calls use Collectors.toList() or Collectors.toSet() as arguments so I think it would be very handy if the Stream interface had default collectToList and collectToList and collectToMap methods.
The advantages are: it would be easier to use code completion in IDEs. There are lot of classes starting with Collect so finding the Collectors class is a bit of a pain. one less method call in what is usually a long chain of calls.
Regards,
Rob Griffin Software Analyst, Spotlight on SQL Server Quest | R&D rob.griffin@quest.com
Hi Rob, i will add to the answer of Brian that if you have too many .collect(toList()), it's perhaps your application perhaps suffers of the equivalent of the n + 1 select query you have with SQL but with the Stream API. You should try to see if returning a Stream instead of a List for some of methods is not a better option: public List<Employee> getAllEmployee() { return employees.stream().filter(Employee::isActive).collect(toList()); } public List<Employee> getManager(List<Employee> list) { return list.stream().filter(Employee::isManager).collect(toList()); } ... getManager(getAllEmployee()); should be: public Stream<Employee> getAllEmployee() { return employees.stream().filter(Employee::isActive); } public Stream<Employee> getManager(Stream<Employee> stream) { return stream.filter(Employee::isManager); } ... getManager(getAllEmployee()).collect(toList()); regards, Rémi ----- Mail original -----
De: "Brian Goetz" <brian.goetz@oracle.com> À: "Rob Griffin (rgriffin)" <Rob.Griffin@quest.com>, "core-libs-dev" <core-libs-dev@openjdk.java.net> Envoyé: Lundi 10 Décembre 2018 17:14:41 Objet: Re: Add convenience collect methods to the Stream interface
As will surprise no one, this was extensively discussed during the development of the Streams API. (Our default position on "convenience methods" is hostile. While everyone sees the benefit of convenience methods (it's convenient!), most people don't see the cost, which includes the complexity for users to understand the model by looking at the API; having lots of ad-hoc convenience method obscures the underlying model, making it harder for everyone to learn or reason about. That default position seems to stand up pretty well here, as the stream API is pretty well factored.)
Let's be honest: the "convenience" or concision of being able to say .toList() instead of .collect(toList()) is really small. I don't think you'll be able to justify it by saying "but we do it a lot." (Digression: to whoever is about to say "then why `toArray()`? Arrays are different; for better or worse, they're part of the language, and they lend themselves particularly poorly to the Collector API, and there are particular parallelization optimizations that are possible for arrays that can't be accessed through Collector. End digression.)
It is possible, however, that one could justify `toList()` on the basis of _discoverability_. (Though I'm having a hard time seeing any world where `toSet()` makes the cut.) New users who approach streams will not easily be able to figure out how to materialize a list from a stream, even though this is an entirely reasonable and quite common thing to want to do. Having to learn about `collect()` first is asking a lot of users who are still wrapping their heads around streams. Not only would `toList()` be more discoverable, it would provide a path to discovery of the rest of the `collect()` API. This is a point in its favor.
A significant downside of adding `toList()` is that by diluting the orthogonality of the existing API, it provides both incentive and justification for further dilution, leading to someplace we don't want to be. (And, the cost of that falls heavily on the stewards, which in turn takes time away from far more valuable activities.)
Put it this way: imagine we have a budget of one convenience method in Stream for every five years. Is this the one we want to spend the next five year's budget on? (And, who of the proponents will volunteer to answer the next 200 "I have an idea for a stream method" mails, explaining that the budget is spent?)
So, summary:
- I won't outright veto `toList`, as I would for almost all other "convenience" streams additions, because this one actually has a valid non-convenience argument; - But, it's still not a slam dunk.
On 12/9/2018 5:44 PM, Rob Griffin (rgriffin) wrote:
Hi,
I have raised an enhancement request (Incident Report 913453) about adding some convenience methods to the Stream interface that collect the stream and Pallavi Sonal asked me to start a thread here about that.
More than 50% of our Stream collect calls use Collectors.toList() or Collectors.toSet() as arguments so I think it would be very handy if the Stream interface had default collectToList and collectToList and collectToMap methods.
The advantages are: it would be easier to use code completion in IDEs. There are lot of classes starting with Collect so finding the Collectors class is a bit of a pain. one less method call in what is usually a long chain of calls.
Regards,
Rob Griffin Software Analyst, Spotlight on SQL Server Quest | R&D rob.griffin@quest.com
Hi Remi, There are certainly places where we could do this when we are simply iterating over the results but that is not always the case. However I was disappointed to find that the enhanced for loop can't iterate over a stream so if callers of your example methods where doing something like this for (Employee emp : getAllEmployee()) { ... } then it would have to change to a forEach call if getAllEmployee returned a Stream. Regards, Rob Griffin Software Analyst, Spotlight on SQL Server Quest | R&D rob.griffin@quest.com Office +613-9811-8021 -----Original Message----- From: Remi Forax <forax@univ-mlv.fr> Sent: Tuesday, 11 December 2018 4:26 AM To: Rob Griffin (rgriffin) <Rob.Griffin@quest.com> Cc: core-libs-dev <core-libs-dev@openjdk.java.net>; Brian Goetz <brian.goetz@oracle.com> Subject: Re: Add convenience collect methods to the Stream interface CAUTION: This email originated from outside of the organization. Do not click links or open attachments unless you recognize the sender and know the content is safe. Hi Rob, i will add to the answer of Brian that if you have too many .collect(toList()), it's perhaps your application perhaps suffers of the equivalent of the n + 1 select query you have with SQL but with the Stream API. You should try to see if returning a Stream instead of a List for some of methods is not a better option: public List<Employee> getAllEmployee() { return employees.stream().filter(Employee::isActive).collect(toList()); } public List<Employee> getManager(List<Employee> list) { return list.stream().filter(Employee::isManager).collect(toList()); } ... getManager(getAllEmployee()); should be: public Stream<Employee> getAllEmployee() { return employees.stream().filter(Employee::isActive); } public Stream<Employee> getManager(Stream<Employee> stream) { return stream.filter(Employee::isManager); } ... getManager(getAllEmployee()).collect(toList()); regards, Rémi ----- Mail original -----
De: "Brian Goetz" <brian.goetz@oracle.com> À: "Rob Griffin (rgriffin)" <Rob.Griffin@quest.com>, "core-libs-dev" <core-libs-dev@openjdk.java.net> Envoyé: Lundi 10 Décembre 2018 17:14:41 Objet: Re: Add convenience collect methods to the Stream interface
As will surprise no one, this was extensively discussed during the development of the Streams API. (Our default position on "convenience methods" is hostile. While everyone sees the benefit of convenience methods (it's convenient!), most people don't see the cost, which includes the complexity for users to understand the model by looking at the API; having lots of ad-hoc convenience method obscures the underlying model, making it harder for everyone to learn or reason about. That default position seems to stand up pretty well here, as the stream API is pretty well factored.)
Let's be honest: the "convenience" or concision of being able to say .toList() instead of .collect(toList()) is really small. I don't think you'll be able to justify it by saying "but we do it a lot." (Digression: to whoever is about to say "then why `toArray()`? Arrays are different; for better or worse, they're part of the language, and they lend themselves particularly poorly to the Collector API, and there are particular parallelization optimizations that are possible for arrays that can't be accessed through Collector. End digression.)
It is possible, however, that one could justify `toList()` on the basis of _discoverability_. (Though I'm having a hard time seeing any world where `toSet()` makes the cut.) New users who approach streams will not easily be able to figure out how to materialize a list from a stream, even though this is an entirely reasonable and quite common thing to want to do. Having to learn about `collect()` first is asking a lot of users who are still wrapping their heads around streams. Not only would `toList()` be more discoverable, it would provide a path to discovery of the rest of the `collect()` API. This is a point in its favor.
A significant downside of adding `toList()` is that by diluting the orthogonality of the existing API, it provides both incentive and justification for further dilution, leading to someplace we don't want to be. (And, the cost of that falls heavily on the stewards, which in turn takes time away from far more valuable activities.)
Put it this way: imagine we have a budget of one convenience method in Stream for every five years. Is this the one we want to spend the next five year's budget on? (And, who of the proponents will volunteer to answer the next 200 "I have an idea for a stream method" mails, explaining that the budget is spent?)
So, summary:
- I won't outright veto `toList`, as I would for almost all other "convenience" streams additions, because this one actually has a valid non-convenience argument; - But, it's still not a slam dunk.
On 12/9/2018 5:44 PM, Rob Griffin (rgriffin) wrote:
Hi,
I have raised an enhancement request (Incident Report 913453) about adding some convenience methods to the Stream interface that collect the stream and Pallavi Sonal asked me to start a thread here about that.
More than 50% of our Stream collect calls use Collectors.toList() or Collectors.toSet() as arguments so I think it would be very handy if the Stream interface had default collectToList and collectToList and collectToMap methods.
The advantages are: it would be easier to use code completion in IDEs. There are lot of classes starting with Collect so finding the Collectors class is a bit of a pain. one less method call in what is usually a long chain of calls.
Regards,
Rob Griffin Software Analyst, Spotlight on SQL Server Quest | R&D rob.griffin@quest.com
Although I do understand the reasoning (non-repeatability of streams), Stream not implementing Iterable is a minor but frequent annoyance for this precise reason. Using forEach() instead isn't always an option. Maybe Iterable can have a superinterface IterableOnce with the methods moved up to there, and iterator() specified to throw IllegalStateException if called twice, and which enhanced-for recognises, then Stream can implement that? (Ducks and runs...) Or enhanced-for can also take Supplier<Iterator>, so we can do 'for (Thing e : stream::iterator)' instead of 'for (Thing e : (Iterable<Thing>)stream::iterator)' (runs faster...) -- Rachel On 10/12/2018 22:11, Rob Griffin (rgriffin) wrote:
Hi Remi,
There are certainly places where we could do this when we are simply iterating over the results but that is not always the case. However I was disappointed to find that the enhanced for loop can't iterate over a stream so if callers of your example methods where doing something like this
for (Employee emp : getAllEmployee()) { ... }
then it would have to change to a forEach call if getAllEmployee returned a Stream.
Regards,
Rob Griffin Software Analyst, Spotlight on SQL Server Quest | R&D rob.griffin@quest.com Office +613-9811-8021
-----Original Message----- From: Remi Forax <forax@univ-mlv.fr> Sent: Tuesday, 11 December 2018 4:26 AM To: Rob Griffin (rgriffin) <Rob.Griffin@quest.com> Cc: core-libs-dev <core-libs-dev@openjdk.java.net>; Brian Goetz <brian.goetz@oracle.com> Subject: Re: Add convenience collect methods to the Stream interface
CAUTION: This email originated from outside of the organization. Do not click links or open attachments unless you recognize the sender and know the content is safe.
Hi Rob, i will add to the answer of Brian that if you have too many .collect(toList()), it's perhaps your application perhaps suffers of the equivalent of the n + 1 select query you have with SQL but with the Stream API.
You should try to see if returning a Stream instead of a List for some of methods is not a better option: public List<Employee> getAllEmployee() { return employees.stream().filter(Employee::isActive).collect(toList()); } public List<Employee> getManager(List<Employee> list) { return list.stream().filter(Employee::isManager).collect(toList()); } ... getManager(getAllEmployee());
should be: public Stream<Employee> getAllEmployee() { return employees.stream().filter(Employee::isActive); } public Stream<Employee> getManager(Stream<Employee> stream) { return stream.filter(Employee::isManager); } ... getManager(getAllEmployee()).collect(toList());
regards, Rémi
----- Mail original -----
De: "Brian Goetz" <brian.goetz@oracle.com> À: "Rob Griffin (rgriffin)" <Rob.Griffin@quest.com>, "core-libs-dev" <core-libs-dev@openjdk.java.net> Envoyé: Lundi 10 Décembre 2018 17:14:41 Objet: Re: Add convenience collect methods to the Stream interface As will surprise no one, this was extensively discussed during the development of the Streams API. (Our default position on "convenience methods" is hostile. While everyone sees the benefit of convenience methods (it's convenient!), most people don't see the cost, which includes the complexity for users to understand the model by looking at the API; having lots of ad-hoc convenience method obscures the underlying model, making it harder for everyone to learn or reason about. That default position seems to stand up pretty well here, as the stream API is pretty well factored.)
Let's be honest: the "convenience" or concision of being able to say .toList() instead of .collect(toList()) is really small. I don't think you'll be able to justify it by saying "but we do it a lot." (Digression: to whoever is about to say "then why `toArray()`? Arrays are different; for better or worse, they're part of the language, and they lend themselves particularly poorly to the Collector API, and there are particular parallelization optimizations that are possible for arrays that can't be accessed through Collector. End digression.)
It is possible, however, that one could justify `toList()` on the basis of _discoverability_. (Though I'm having a hard time seeing any world where `toSet()` makes the cut.) New users who approach streams will not easily be able to figure out how to materialize a list from a stream, even though this is an entirely reasonable and quite common thing to want to do. Having to learn about `collect()` first is asking a lot of users who are still wrapping their heads around streams. Not only would `toList()` be more discoverable, it would provide a path to discovery of the rest of the `collect()` API. This is a point in its favor.
A significant downside of adding `toList()` is that by diluting the orthogonality of the existing API, it provides both incentive and justification for further dilution, leading to someplace we don't want to be. (And, the cost of that falls heavily on the stewards, which in turn takes time away from far more valuable activities.)
Put it this way: imagine we have a budget of one convenience method in Stream for every five years. Is this the one we want to spend the next five year's budget on? (And, who of the proponents will volunteer to answer the next 200 "I have an idea for a stream method" mails, explaining that the budget is spent?)
So, summary:
- I won't outright veto `toList`, as I would for almost all other "convenience" streams additions, because this one actually has a valid non-convenience argument; - But, it's still not a slam dunk.
On 12/9/2018 5:44 PM, Rob Griffin (rgriffin) wrote:
Hi,
I have raised an enhancement request (Incident Report 913453) about adding some convenience methods to the Stream interface that collect the stream and Pallavi Sonal asked me to start a thread here about that.
More than 50% of our Stream collect calls use Collectors.toList() or Collectors.toSet() as arguments so I think it would be very handy if the Stream interface had default collectToList and collectToList and collectToMap methods.
The advantages are: it would be easier to use code completion in IDEs. There are lot of classes starting with Collect so finding the Collectors class is a bit of a pain. one less method call in what is usually a long chain of calls.
Regards,
Rob Griffin Software Analyst, Spotlight on SQL Server Quest | R&D rob.griffin@quest.com
You say it jokingly, but we've explored this. (The exact way you phrase it (the supertype specified to throw if called more than once) means that Iterable can't extend IterableOnce, because then Iterable does not conform to the superclass contract, but there are other ways to stack it.) It's not completely unworkable, but as you've observed, it's not so clean. In the JSR-335 EG, we also explored (and rejected, because it felt like the library tail wagging the language dog) the route of adding language support for more kinds of things on the RHS of the for-each loop. (Zooming out: the main reason this is irritating is the limitations of what you can do in a method like .forEach() vs the foreach loop -- exception transparency, nonlocal return, up-scope local mutation. Though it is unlikely that we'll have great solutions for these all that soon, so its reasonable to consider library-based solutions in the meantime.) On 12/11/2018 7:55 AM, Rachel Greenham wrote:
Although I do understand the reasoning (non-repeatability of streams), Stream not implementing Iterable is a minor but frequent annoyance for this precise reason. Using forEach() instead isn't always an option.
Maybe Iterable can have a superinterface IterableOnce with the methods moved up to there, and iterator() specified to throw IllegalStateException if called twice, and which enhanced-for recognises, then Stream can implement that? (Ducks and runs...)
Or enhanced-for can also take Supplier<Iterator>, so we can do 'for (Thing e : stream::iterator)' instead of 'for (Thing e : (Iterable<Thing>)stream::iterator)'
(runs faster...)
Hi Rob, On 12/10/18 11:11 PM, Rob Griffin (rgriffin) wrote:
Hi Remi,
There are certainly places where we could do this when we are simply iterating over the results but that is not always the case. However I was disappointed to find that the enhanced for loop can't iterate over a stream so if callers of your example methods where doing something like this
for (Employee emp : getAllEmployee()) { ... }
then it would have to change to a forEach call if getAllEmployee returned a Stream.
You can also get an Iterator from a Stream, so if you need external iteration over elements of a Stream you don't have to collect it 1st to some Collection: Stream<String> names() { return Stream.of("John", "Jil", "Jack"); } ...and then... for (String name : (Iterable<String>) names()::iterator) { System.out.println(name); } This is hack-ish as it relies on the fact that enhanced for loop calls Iterable.iterator() method only once, but is the only way to do it if you already have a reference to Stream at hand. This would be more correct way of doing it if you can call a factory for Stream: for (String name : (Iterable<String>) () -> names().iterator()) { System.out.println(name); } Regards, Peter P.S. I wonder why the enhanced for loop doesn't establish a context where the type of expression after the colon could be inferred, so no cast would be necessary. Perhaps because that type could either be an Iterable<T> or a T[] ?
Hi Rob, Hi Petter, we (the lambda EG) have decided against implementing Iterable (see the answer of Brian), so for consistency we have also not enable the right hand side of a enhanced for loop to be a poly-expression (that enables the lambda conversion), hence the mandatory cast. Rémi ----- Mail original -----
De: "Peter Levart" <peter.levart@gmail.com> À: "Rob Griffin, rgriffin" <Rob.Griffin@quest.com>, "Remi Forax" <forax@univ-mlv.fr> Cc: "core-libs-dev" <core-libs-dev@openjdk.java.net> Envoyé: Mardi 11 Décembre 2018 14:42:51 Objet: Re: Add convenience collect methods to the Stream interface
Hi Rob,
On 12/10/18 11:11 PM, Rob Griffin (rgriffin) wrote:
Hi Remi,
There are certainly places where we could do this when we are simply iterating over the results but that is not always the case. However I was disappointed to find that the enhanced for loop can't iterate over a stream so if callers of your example methods where doing something like this
for (Employee emp : getAllEmployee()) { ... }
then it would have to change to a forEach call if getAllEmployee returned a Stream.
You can also get an Iterator from a Stream, so if you need external iteration over elements of a Stream you don't have to collect it 1st to some Collection:
Stream<String> names() { return Stream.of("John", "Jil", "Jack"); }
...and then...
for (String name : (Iterable<String>) names()::iterator) { System.out.println(name); }
This is hack-ish as it relies on the fact that enhanced for loop calls Iterable.iterator() method only once, but is the only way to do it if you already have a reference to Stream at hand. This would be more correct way of doing it if you can call a factory for Stream:
for (String name : (Iterable<String>) () -> names().iterator()) { System.out.println(name); }
Regards, Peter
P.S. I wonder why the enhanced for loop doesn't establish a context where the type of expression after the colon could be inferred, so no cast would be necessary. Perhaps because that type could either be an Iterable<T> or a T[] ?
And i've forgotten to add that currently calling stream.iterator() is sloooow, i don't know if it's just because the implementation of this method did not get some love or it's a more fundamental issue because a spliterator is push while an iterator is pull so the implementation needs an intermediary memory to store the element in between. Rémi ----- Mail original -----
De: "Remi Forax" <forax@univ-mlv.fr> À: "Peter Levart" <peter.levart@gmail.com> Cc: "Rob Griffin, rgriffin" <Rob.Griffin@quest.com>, "core-libs-dev" <core-libs-dev@openjdk.java.net> Envoyé: Mardi 11 Décembre 2018 15:18:24 Objet: Re: Add convenience collect methods to the Stream interface
Hi Rob, Hi Petter, we (the lambda EG) have decided against implementing Iterable (see the answer of Brian), so for consistency we have also not enable the right hand side of a enhanced for loop to be a poly-expression (that enables the lambda conversion), hence the mandatory cast.
Rémi
----- Mail original -----
De: "Peter Levart" <peter.levart@gmail.com> À: "Rob Griffin, rgriffin" <Rob.Griffin@quest.com>, "Remi Forax" <forax@univ-mlv.fr> Cc: "core-libs-dev" <core-libs-dev@openjdk.java.net> Envoyé: Mardi 11 Décembre 2018 14:42:51 Objet: Re: Add convenience collect methods to the Stream interface
Hi Rob,
On 12/10/18 11:11 PM, Rob Griffin (rgriffin) wrote:
Hi Remi,
There are certainly places where we could do this when we are simply iterating over the results but that is not always the case. However I was disappointed to find that the enhanced for loop can't iterate over a stream so if callers of your example methods where doing something like this
for (Employee emp : getAllEmployee()) { ... }
then it would have to change to a forEach call if getAllEmployee returned a Stream.
You can also get an Iterator from a Stream, so if you need external iteration over elements of a Stream you don't have to collect it 1st to some Collection:
Stream<String> names() { return Stream.of("John", "Jil", "Jack"); }
...and then...
for (String name : (Iterable<String>) names()::iterator) { System.out.println(name); }
This is hack-ish as it relies on the fact that enhanced for loop calls Iterable.iterator() method only once, but is the only way to do it if you already have a reference to Stream at hand. This would be more correct way of doing it if you can call a factory for Stream:
for (String name : (Iterable<String>) () -> names().iterator()) { System.out.println(name); }
Regards, Peter
P.S. I wonder why the enhanced for loop doesn't establish a context where the type of expression after the colon could be inferred, so no cast would be necessary. Perhaps because that type could either be an Iterable<T> or a T[] ?
FTR: already tracking as JDK-8180352. On 12/9/2018 5:44 PM, Rob Griffin (rgriffin) wrote:
Hi,
I have raised an enhancement request (Incident Report 913453) about adding some convenience methods to the Stream interface that collect the stream and Pallavi Sonal asked me to start a thread here about that.
More than 50% of our Stream collect calls use Collectors.toList() or Collectors.toSet() as arguments so I think it would be very handy if the Stream interface had default collectToList and collectToList and collectToMap methods.
The advantages are: it would be easier to use code completion in IDEs. There are lot of classes starting with Collect so finding the Collectors class is a bit of a pain. one less method call in what is usually a long chain of calls.
Regards,
Rob Griffin Software Analyst, Spotlight on SQL Server Quest | R&D rob.griffin@quest.com
participants (7)
-
Brian Goetz
-
forax@univ-mlv.fr
-
James Roper
-
Peter Levart
-
Rachel Greenham
-
Remi Forax
-
Rob Griffin (rgriffin)