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