Proposal: JDK-8148917 Enhanced-For Statement Should Allow Streams
John Rose
john.r.rose at oracle.com
Mon Mar 11 23:07:47 UTC 2019
On Mar 7, 2019, at 4:33 AM, Tagir Valeev <amaembo at gmail.com> wrote:
>
> Hello, Remi!
>
> It actually works thanks to auto-boxing/unboxing. E.g. this
> implementation works:
>
> static Iterable<Integer> range(int from, int to) {
> return () -> new PrimitiveIterator.OfInt() {
> int cur = from;
>
> @Override
> public int nextInt() {
> if (cur >= to) {
> throw new NoSuchElementException();
> }
> return cur++;
> }
>
> @Override
> public boolean hasNext() {
> return cur < to;
> }
> };
> }
>
> public static void main(String[] args) {
> for(int i : range(0, 100)) {
> System.out.println(i);
> }
> }
>
> It correctly compiles and prints numbers from 0 to 99. As IntStream
> extends BaseStream<Integer, IntStream> and BaseStream<T, S extends
> BaseStream<T, S>> defines Iterator<T> iterator(), it would be no
> problem with using IntStream.range in such code pattern were
> BaseStream extends IterableOnce<T>.
>
> Of course this produces unnecessary garbage, as I said.
This is a relatively simple kind of garbage to remove, because
it is made (by calls to Integer.valueOf) at the adapted boundaries
of the iterator, which are readily inlined into the loop. The deeper
internal logic of the range function is box-free, as is the loop itself,
so the garbage is relatively easy to remove.
That said, "out of the box" there is lots of garbage created unless
-XX:+AggressiveUnboxing is turned on. I assume Graal does a good
job on it, even without this switch.
If we ever succeed in suppressing the identity of java.lang.Integer,
and/or after making functions like range into reified generics, the
boxing will go away even more directly and simply.
So, from the JIT engineering point of view, I would classify this
particular boxing problem as relatively short-lived.
— John
P.S. I also saw the fiery objections to the range() idiom, and I have
to disagree with those disagreers! I prefer the range idiom to the
multi-part for-init-test-step idiom, because counters are semantically
*more complex* than ranges, from the viewpoints I typically use to
reason about programs. (There are times when a separate counter
state *is* preferable to me, but usually when there is some sort of
extra "i += skip" side effect in the loop.) That's got to be a matter of
taste. I suppose some people habitually reason about programs
in lower-level terms, like x86 instructions, and there a counter is no
more complex than a range; for those people there's no reason to
learn a different concept than a for-loop. And surely there are other
points of view that favor the good old for-loop-with-int-counter.
I'm more tolerant of new-fangled range-based notations because they
let me avoid having to reason about side-effect-laden entities
like counters, and that feels like a good thing to me. Tastes vary.
>
> With best regards,
> Tagir Valeev.
>
> On Wed, Mar 6, 2019 at 7:37 PM Remi Forax <forax at univ-mlv.fr> wrote:
>>
>> Hi Tagir,
>> try to do it now and you will see that you can't, because you can not write Iterable<int> yet.
>> Once we will get generics over value types, it will be a no-brainer.
>>
>> Rémi
>>
>> ----- Mail original -----
>>> De: "Tagir Valeev" <amaembo at gmail.com>
>>> À: "Stuart Marks" <stuart.marks at oracle.com>
>>> Cc: "core-libs-dev" <core-libs-dev at openjdk.java.net>
>>> Envoyé: Mercredi 6 Mars 2019 11:10:41
>>> Objet: Re: Proposal: JDK-8148917 Enhanced-For Statement Should Allow Streams
>>
>>> Hello!
>>>
>>> By the way one of the painful code patterns in modern Java is `for(int
>>> i = 0; i<bound; i++)` which is very verbose, hackish, confusing for
>>> newbies and prone to errors as the variable need to be repeated three
>>> times. Also the variable is not effectively final, despite it never
>>> changes within the loop body, so could have been considered as
>>> redeclared on every loop iteration (like in for-each). With the new
>>> proposal it's possible to write `for(int i : range(0, bound).boxed())`
>>> (assuming import static j.u.s.IntStream.range), which looks much
>>> better, though it has obvious performance drawback. Moving
>>> IterableOnce to BaseStream would enable to use `for(int i : range(0,
>>> bound))` which looks even better, though again we have plenty of
>>> garbage (but considerably less than in previous case!). I wonder
>>> whether Java could evolve to the point where such kind of code would
>>> be a recommended way to iterate over subsequent integer values without
>>> any performance handicap.
>>>
>>> With best regards,
>>> Tagir Valeev.
>>>
>>> On Fri, Mar 1, 2019 at 9:47 AM Stuart Marks <stuart.marks at oracle.com> wrote:
>>>>
>>>> Hi all,
>>>>
>>>> Please review and comment on this proposal to allow Stream instances to be used
>>>> in enhanced-for ("for-each") loops.
>>>>
>>>> Abstract
>>>>
>>>> Occasionally it's useful to iterate a Stream using a conventional loop. However,
>>>> the Stream interface doesn't implement Iterable, and therefore streams cannot be
>>>> used with the enhanced-for statement. This is a proposal to remedy that
>>>> situation by introducing a new interface IterableOnce that is a subtype of
>>>> Iterable, and then retrofitting the Stream interface to implement it. Other JDK
>>>> classes will also be retrofitted to implement IterableOnce.
>>>>
>>>> Full Proposal:
>>>>
>>>> http://cr.openjdk.java.net/~smarks/reviews/8148917/IterableOnce0.html
>>>>
>>>> Bug report:
>>>>
>>>> https://bugs.openjdk.java.net/browse/JDK-8148917
>>>>
>>>> Webrev:
>>>>
>>>> http://cr.openjdk.java.net/~smarks/reviews/8148917/webrev.0/
>>>>
>>>> Note, this changeset isn't ready to push yet. In particular, it has no tests
>>>> yet. However, the implementation is so simple that I figured I should include
>>>> it. Comments on the specification wording are also welcome.
>>>>
>>>> Thanks,
>>>>
>>>> s'marks
More information about the core-libs-dev
mailing list