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

Tagir F. Valeev amaembo at gmail.com
Wed Feb 17 11:58:32 UTC 2016


Hello!

Thank you for your comments. The thing which is absolutely necessary
is to make PECS Predicate signature (for some reason I forgot it):

static <T> Stream<T> iterate(T seed, Predicate<? super T> predicate, UnaryOperator<T> f) {
    ...
}

While you cannot readily make Stream<Number> from it, it's not a big
problem if the stream has more specific type. For example, you still
can collect to the List<Number>. This code works perfectly:

List<Number> list = iterate(0, i -> i < 10, i -> i + 1).collect(Collectors.toList());
Predicate<CharSequence> csNotEmpty = cs -> cs.length() > 0;
List<CharSequence> result = iterate("abcde", csNotEmpty, (String s) -> s.substring(1))
    .collect(Collectors.toList());

Any functions appearing in the intermediate/terminal operations should
also be fine with more concrete type (even if they are specified not
as lambdas, but as actual interfaces like Function<X, Y>). The only
problem which iterate3 would solve is when you actually need the
stream of particular type (for example, you're implementing the
interface method which returns exactly Stream<Number>). I think it's
quite rare case and you can always add map step to solve this (if you
don't like unchecked cast):

Stream<Number> sn4 = iterate(0, i -> i < 10, i -> i + 1).map(Number.class::cast);

Probably it's still reasonable to introduce second type variable, but
for conformance with existing signatures of Stream.generate,
Stream.iterate I would prefer not to stay with <T> only. Or probably
all these methods should be updated at once (this could be considered
as separate issue).

With best regards,
Tagir Valeev.

PL> On 02/14/2016 07:15 PM, Stefan Zobel       wrote:
PL>     
PL>   
PL> Hi Tagir,

PL> this looks good. I wonder, however, if the signature should
PL> accept a wider range of types, i.e., something like


PL> static <T, S extends T> Stream<T> iterate(S seed, Predicate<S>
PL> predicate, UnaryOperator<S> f)


PL> I know that the corresponding

PL> static <T> Stream<T> iterate(T seed, UnaryOperator<T> f)

PL> is less general than that, but I don't know whether this is
PL> the outcome of a thoughtful decision or just an oversight.

PL> What do you think?
PL>   
PL>   
PL>      What about even more permissive:
PL>   
PL>      static <T, S extends T> Stream<T> iterate3(S seed,    
PL> Predicate<? super S> predicate, Function<? super S, ?     extends S> f)
PL>   
PL>      ...
PL>   
PL>      public class SignatureTest {
PL>   
PL>          static <T> Stream<T> iterate1(T seed,     Predicate<T> predicate, UnaryOperator<T> f) {
PL>              ...
PL>          }
PL>   
PL>          static <T, S extends T> Stream<T> iterate2(S seed,    
PL> Predicate<S> predicate, UnaryOperator<S> f) {
PL>              ...
PL>          }
PL>   
PL>          static <T, S extends T> Stream<T> iterate3(S seed,    
PL> Predicate<? super S> predicate, Function<? super S, ?     extends S> f) {
PL>              ...
PL>          }
PL>   
PL>   
PL>          public static void main(String[] args) {
PL>   
PL>              Stream<Integer> si1 = iterate1(0, i -> i < 10, i     -> i + 1); // OK
PL>              Stream<Integer> si2 = iterate2(0, i -> i < 10, i     -> i + 1); // OK
PL>              Stream<Integer> si3 = iterate3(0, i -> i < 10, i     -> i + 1); // OK
PL>   
PL>              Stream<Number> sn1 = iterate1(0, i -> i < 10, i     -> i + 1);
PL>   
PL>              //SignatureTest.java:32: error: bad operand types for binary     operator '<'
PL>              //        Stream<Number> sn1 = iterate1(0, i -> i    < 10, i -> i + 1);
PL>              //                                                ^
PL>              //SignatureTest.java:32: error: bad operand types for binary     operator '+'
PL>              //        Stream<Number> sn1 = iterate1(0, i -> i    < 10, i -> i + 1);
PL>                  //                                              
PL>   
PL>   
PL>              Stream<Number> sn2 = iterate2(0, i -> i < 10, i     -> i + 1); // OK
PL>              Stream<Number> sn3 = iterate3(0, i -> i < 10, i     -> i + 1); // OK
PL>   
PL>   
PL>              Predicate<CharSequence> csNotEmpty = cs ->     cs.length() > 0;
PL>   
PL>              Stream<CharSequence> css1 = iterate1("abcde",    
PL> csNotEmpty, (String s) -> s.substring(1));
PL>   
PL>              //SignatureTest.java:44: error: method iterate1 in
PL> class     SignatureTest cannot be applied to given types;
PL>              //        Stream<CharSequence> css1 =    
PL> iterate1("abcde", csNotEmpty, (String s) -> s.substring(1));
PL>              //                                    ^
PL>              //  required: T,Predicate<T>,UnaryOperator<T>
PL>              //  found: String,Predicate<CharSequence>,(String    s)[...]ng(1)
PL>              //  reason: inference variable T has incompatible
PL> equality     constraints String,CharSequence
PL>              //  where T is a type-variable:
PL>              //    T extends Object declared in method    
PL> <T>iterate1(T,Predicate<T>,UnaryOperator<T>)
PL>   
PL>   
PL>              Stream<CharSequence> css2 = iterate2("abcde",    
PL> csNotEmpty, (String s) -> s.substring(1));
PL>   
PL>              //SignatureTest.java:54: error: method iterate2 in
PL> class     SignatureTest cannot be applied to given types;
PL>              //        Stream<CharSequence> css2 =    
PL> iterate2("abcde", csNotEmpty, (String s) -> s.substring(1));
PL>              //                                    ^
PL>              //  required: S,Predicate<S>,UnaryOperator<S>
PL>              //  found: String,Predicate<CharSequence>,(String    s)[...]ng(1)
PL>              //  reason: inference variable S has incompatible
PL> equality     constraints String,CharSequence
PL>              //  where S,T are type-variables:
PL>              //    S extends T declared in method    
PL> <T,S>iterate2(S,Predicate<S>,UnaryOperator<S>)
PL>              //    T extends Object declared in method    
PL> <T,S>iterate2(S,Predicate<S>,UnaryOperator<S>)
PL>   
PL>   
PL>              Stream<CharSequence> css3 = iterate3("abcde",    
PL> csNotEmpty, (String s) -> s.substring(1)); // OK
PL>          }
PL>      }
PL>   
PL>   
PL>      Regards, Peter
PL>   
PL>   
PL>   


PL> Regards,
PL> Stefan


PL> 2016-02-14 15:53 GMT+01:00 Tagir F. Valeev <amaembo at gmail.com>:

PL>   
PL>   
PL> Hello!

PL> I wanted to work on foldLeft, but Brian asked me to take this issue
PL> instead. So here's webrev:
PL> http://cr.openjdk.java.net/~tvaleev/webrev/8072727/r1/

PL> I don't like iterator-based Stream source implementations, so I made
PL> them AbstractSpliterator-based. I also implemented manually
PL> forEachRemaining as, I believe, this improves the performance in
PL> non-short-circuiting cases.

PL> I also decided to keep two flags (started and finished) to track the
PL> state. Currently existing implementation of infinite iterate() does
PL> not use started flag, but instead reads one element ahead for
PL> primitive streams. This seems wrong to me and may even lead to
PL> unexpected exceptions (*). I could get rid of "started" flag for
PL> Stream.iterate() using Streams.NONE, but this would make object
PL> implementation different from primitive implementations. It would also
PL> be possible to keep single three-state variable (byte or int,
PL> NOT_STARTED, STARTED, FINISHED), but I doubt that this would improve
PL> the performance or footprint. Having two flags looks more readable to
PL> me.

PL> Currently existing two-arg iterate methods can now be expressed as a
PL> partial case of the new method:

PL> public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) {
PL>     return iterate(seed, x -> true, f);
PL> }
PL> (same for primitive streams). I may do this if you think it's
PL> reasonable.

PL> I created new test class and added new iterate sources to existing
PL> data providers.

PL> Please review and sponsor!

PL> With best regards,
PL> Tagir Valeev.

PL> (*) Consider the following code:

PL> int[] data = {1,2,3,4,-1};
PL> IntStream.iterate(0, x -> data[x])
PL>          .takeWhile(x -> x >= 0)
PL>          .forEach(System.out::println);

PL> Currently this unexpectedly throws an AIOOBE, because
PL> IntStream.iterate unnecessarily tries to read one element ahead.


PL>   
PL>   
PL>   
PL>    




More information about the core-libs-dev mailing list