This question already has an answer here:
-
Limit a stream by a predicate
18 answers
I am writing a code to calculate Fibonacci numbers. With this code I can generate first n numbers of the Fibonacci sequence.
Stream.generate(new Supplier<Long>() {
private long n1 = 1;
private long n2 = 2;
@Override
public Long get() {
long fibonacci = n1;
long n3 = n2 + n1;
n1 = n2;
n2 = n3;
return fibonacci;
}
}).limit(50).forEach(System.out::println);
The method limit
returns the Stream
which holds the number of elements passed to this method. I want to stop the generation of the Stream
after the Fibonacci number reached some value.
I mean if I want to list all Fibonacci numbers less than 1000 then I cannot use limit
, because I don't know how many Fibonacci numbers there could be.
Is there any way to do this using lambda expressions?
If you don't mind using an iterator, you can write it as:
static LongUnaryOperator factorial = x -> x == 0 ? 1
: x * factorial.applyAsLong(x - 1);
public static void main(String[] args) {
LongStream ls = LongStream.iterate(0, i -> i + 1).map(factorial);
OfLong it = ls.iterator();
long next = 0;
while ((next = it.nextLong()) <= 1000) System.out.println(next);
}
The best solution using the Stream
’s built-in features I could find is:
LongStream.generate(new LongSupplier() {
private long n1 = 1, n2 = 2;
public long getAsLong() {
long fibonacci = n1;
long n3 = n2 + n1;
n1 = n2;
n2 = n3;
return fibonacci;
}
}).peek(System.out::println).filter(x->x>1000).findFirst();
It has the disadvantage of processing the first item being >=1000
though. This can be prevented by making the statement conditional, e.g.
.peek(x->{if(x<=1000) System.out.println(x);}).filter(x->x>1000).findFirst();
but I don’t like to evaluate the same condition (bigger than thousand or not) twice. But maybe one of these two solution might be practical enough for real life tasks where a limit based on the resulting value is needed.
I think, it’s clear that the entire construct is not parallel capable…
Yes, there is a lambda way but unfortunately, I don't think it is implemented in the current Java 8 Stream
API.
Sorry to point you to a different language, but what I think you want is something like
takeWhile(p: (A) ⇒ Boolean): Stream[A]
from the Scala Stream API.
As this is not implemented in the Java API, you have to do it yourself.
How about this one:
public static List<T> takeWhile(Iterable<T> elements, Predicate<T> predicate) {
Iterator<T> iter = elements.iterator();
List<T> result = new LinkedList<T>();
while(iter.hasNext()) {
T next = iter.next();
if (predicate.apply(next)) {
result.add(next);
} else {
return result; // Found first one not matching: abort
}
}
return result; // Found end of the elements
}
Then you could use it like
List<Long> fibNumbersUnderThousand = takeWhile(allFibNumStream, l -> l < 1000);
(Assuming that Stream
is an instance of Iterable
- if not, you might need to call the .iterator()
method and wrap that up)
Dirty first version
Stream.generate(new Supplier<Long>() {
private long n1 = 1;
private long n2 = 2;
@Override
public Long get() {
long fibonacci = n1;
long n3 = n2 + n1;
n1 = n2;
n2 = n3;
return fibonacci;
}
}).limit(50).forEach(x -> {
if (x < 1000) {
System.out.println(x);
}
});