RFR: 8072727 - add variation of Stream.iterate() that's finite
Peter Levart
peter.levart at gmail.com
Mon Feb 15 06:48:42 UTC 2016
On 02/14/2016 07:15 PM, Stefan Zobel wrote:
> Hi Tagir,
>
> this looks good. I wonder, however, if the signature should
> accept a wider range of types, i.e., something like
>
>
> static <T, S extends T> Stream<T> iterate(S seed, Predicate<S>
> predicate, UnaryOperator<S> f)
>
>
> I know that the corresponding
>
> static <T> Stream<T> iterate(T seed, UnaryOperator<T> f)
>
> is less general than that, but I don't know whether this is
> the outcome of a thoughtful decision or just an oversight.
>
> What do you think?
What about even more permissive:
static <T, S extends T> Stream<T> iterate3(S seed, Predicate<? super S>
predicate, Function<? super S, ? extends S> f)
...
public class SignatureTest {
static <T> Stream<T> iterate1(T seed, Predicate<T> predicate,
UnaryOperator<T> f) {
...
}
static <T, S extends T> Stream<T> iterate2(S seed, Predicate<S>
predicate, UnaryOperator<S> f) {
...
}
static <T, S extends T> Stream<T> iterate3(S seed, Predicate<?
super S> predicate, Function<? super S, ? extends S> f) {
...
}
public static void main(String[] args) {
Stream<Integer> si1 = iterate1(0, i -> i < 10, i -> i + 1); // OK
Stream<Integer> si2 = iterate2(0, i -> i < 10, i -> i + 1); // OK
Stream<Integer> si3 = iterate3(0, i -> i < 10, i -> i + 1); // OK
Stream<Number> sn1 = iterate1(0, i -> i < 10, i -> i + 1);
//SignatureTest.java:32: error: bad operand types for binary
operator '<'
// Stream<Number> sn1 = iterate1(0, i -> i < 10, i -> i
+ 1);
// ^
//SignatureTest.java:32: error: bad operand types for binary
operator '+'
// Stream<Number> sn1 = iterate1(0, i -> i < 10, i -> i
+ 1);
// ^
Stream<Number> sn2 = iterate2(0, i -> i < 10, i -> i + 1); // OK
Stream<Number> sn3 = iterate3(0, i -> i < 10, i -> i + 1); // OK
Predicate<CharSequence> csNotEmpty = cs -> cs.length() > 0;
Stream<CharSequence> css1 = iterate1("abcde", csNotEmpty,
(String s) -> s.substring(1));
//SignatureTest.java:44: error: method iterate1 in class
SignatureTest cannot be applied to given types;
// Stream<CharSequence> css1 = iterate1("abcde",
csNotEmpty, (String s) -> s.substring(1));
// ^
// required: T,Predicate<T>,UnaryOperator<T>
// found: String,Predicate<CharSequence>,(String s)[...]ng(1)
// reason: inference variable T has incompatible equality
constraints String,CharSequence
// where T is a type-variable:
// T extends Object declared in method
<T>iterate1(T,Predicate<T>,UnaryOperator<T>)
Stream<CharSequence> css2 = iterate2("abcde", csNotEmpty,
(String s) -> s.substring(1));
//SignatureTest.java:54: error: method iterate2 in class
SignatureTest cannot be applied to given types;
// Stream<CharSequence> css2 = iterate2("abcde",
csNotEmpty, (String s) -> s.substring(1));
// ^
// required: S,Predicate<S>,UnaryOperator<S>
// found: String,Predicate<CharSequence>,(String s)[...]ng(1)
// reason: inference variable S has incompatible equality
constraints String,CharSequence
// where S,T are type-variables:
// S extends T declared in method
<T,S>iterate2(S,Predicate<S>,UnaryOperator<S>)
// T extends Object declared in method
<T,S>iterate2(S,Predicate<S>,UnaryOperator<S>)
Stream<CharSequence> css3 = iterate3("abcde", csNotEmpty,
(String s) -> s.substring(1)); // OK
}
}
Regards, Peter
>
> Regards,
> Stefan
>
>
> 2016-02-14 15:53 GMT+01:00 Tagir F. Valeev <amaembo at gmail.com>:
>> 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