RFR: 8072727 - add variation of Stream.iterate() that's finite

Paul Sandoz paul.sandoz at oracle.com
Mon Feb 15 18:08:46 UTC 2016


> On 15 Feb 2016, at 18:39, Brian Goetz <brian.goetz at oracle.com> wrote:
> 
> Overall the implementation choices are sound and the testing story is nicely integrated into the existing test framework.
> 
> For the spec, I'd like to make the connection with the for-loop, and I'd like to clarify that this may produce an empty stream (if predicate.test(seed) is false.)
> 

Yes, +1, that is the behaviour i expect, but it could be misconstrued without such clarification.

I will take a closer look at the webrev tomorrow, i am not sure we need such test integration within the data providers, we have to be careful when increasing the overall time of test execution.

—

Note that the example in (*) is somewhat concocted :-) the function needs to work correctly for an infinite sequence, and not be "cognisant” of what operations follow downstream, in that sense it is ok to be aggressive, and parallel execution may also consume more elements than is strictly necessary.

For the three arg case we need exact control; the emulation iterate(s, uo).takeWhile(p) is not always sufficient, nor necessarily efficient.

Paul.

> /**
> 1208 * Returns a sequential ordered {@code Stream} produced by iterative
> 1209 * application of a function {@code f} to an initial element {@code seed},
> 1210 * producing a {@code Stream} consisting of {@code seed}, {@code f(seed)},
> 1211 * {@code f(f(seed))}, etc. The stream terminates when {@code predicate}
> 1212 * returns false.
> 1213 *
> 1214 * <p>The first element (position {@code 0}) in the {@code Stream} will be
> 1215 * the provided {@code seed}. For {@code n > 0}, the element at position
> 1216 * {@code n}, will be the result of applying the function {@code f} to the
> 1217 * element at position {@code n - 1}.
> 1218 *
> 1219 * @param <T> the type of stream elements
> 1220 * @param seed the initial element
> 1221 * @param predicate a predicate to apply to elements to determine when the
> 1222 * stream must terminate.
> 1223 * @param f a function to be applied to the previous element to produce
> 1224 * a new element
> 1225 * @return a new sequential {@code Stream}
> 1226 * @since 9
> 1227 */
> 
> 
> Something like:
> 
> Returns a sequential ordered Stream produced by iterative application of a function to an initial element, conditioned on satisfying the supplied predicate.  The stream terminates as soon as the predicate function returns false.  Stream.iterate should produce the same sequence of elements as produced by the corresponding for-loop:
> 
>    for (T index=seed; predicate.test(index); index = f.apply(index)) { ... }
> 
> The resulting sequence may be empty if the predicate does not hold on the seed value.  Otherwise the first element will be the supplied seed value, the next element (if present) will be the result of applying the function f to the seed value, and so on iteratively until the predicate indicates that the stream should terminate.
> 
> 
> On 2/14/2016 9:53 AM, Tagir F. Valeev wrote:
>> Hello!
>> 
>> I wanted to work on foldLeft, but Brian asked me to take this issue
>> instead. So here's webrev:
>> http://cr.openjdk.java.net/~tvaleev/webrev/8072727/r1/
>> 
>> I don't like iterator-based Stream source implementations, so I made
>> them AbstractSpliterator-based. I also implemented manually
>> forEachRemaining as, I believe, this improves the performance in
>> non-short-circuiting cases.
>> 
>> I also decided to keep two flags (started and finished) to track the
>> state. Currently existing implementation of infinite iterate() does
>> not use started flag, but instead reads one element ahead for
>> primitive streams. This seems wrong to me and may even lead to
>> unexpected exceptions (*). I could get rid of "started" flag for
>> Stream.iterate() using Streams.NONE, but this would make object
>> implementation different from primitive implementations. It would also
>> be possible to keep single three-state variable (byte or int,
>> NOT_STARTED, STARTED, FINISHED), but I doubt that this would improve
>> the performance or footprint. Having two flags looks more readable to
>> me.
>> 
>> Currently existing two-arg iterate methods can now be expressed as a
>> partial case of the new method:
>> 
>> public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) {
>>     return iterate(seed, x -> true, f);
>> }
>> (same for primitive streams). I may do this if you think it's
>> reasonable.
>> 
>> I created new test class and added new iterate sources to existing
>> data providers.
>> 
>> Please review and sponsor!
>> 
>> With best regards,
>> Tagir Valeev.
>> 
>> (*) Consider the following code:
>> 
>> int[] data = {1,2,3,4,-1};
>> IntStream.iterate(0, x -> data[x])
>>          .takeWhile(x -> x >= 0)
>>          .forEach(System.out::println);
>> 
>> Currently this unexpectedly throws an AIOOBE, because
>> IntStream.iterate unnecessarily tries to read one element ahead.
>> 
> 




More information about the core-libs-dev mailing list