I'm looking for a way to emulate the following behavior with Java 8 streams. Given a stream of years,sort them so the top 10 values are outputed, such that after outputing a year, that is decreased and the iteration restarts resorting again:
If I input years 2005, 2020, 2000, 1967 and 2018 I expect the following results for a limit of 10:
2020
2019
2018 2018
2017 2017
2016 2016 2016
2015 ...
The test I am using is:
public class LazyTest {
public static void main(String[] args) {
Stream.of(2005,2020,2000,1967,2018)
.map(YearWrapper::new)
.sorted()
.limit(10)
.peek(year -> year.decreaseYear())
.forEach(System.out::println);
}
public static class YearWrapper implements Comparable<YearWrapper>{
private int currentYear;
public YearWrapper(int year){
this.currentYear=year;
}
public void decreaseYear(){
currentYear--;
}
@Override
public int compareTo(YearWrapper yearsToCompare) {
return Integer.compare(yearsToCompare.currentYear, this.currentYear);
}
@Override
public String toString(){
return String.valueOf(currentYear);
}
}
}
But it seems sorted() is not lazy at all. The whole sorting is done once at the beggining so the order is calculated before any further operation and therefore, the 5 values of the example are passed ordered one by one, so decrease() have no real effect on the iteration.
Is there any way to make sorted() lazy and being applied again to the remaining elements before streaming the next one?
Any other close approach would be much appreciated!!
The documentation of Stream.sorted()
says:
This is a stateful intermediate operation.
which is in turn described as
Stateful operations may need to process the entire input before producing a result. For example, one cannot produce any results from sorting a stream until one has seen all elements of the stream.
This documents the non-lazy nature of sorting, however, this has nothing to do with your problem. Even if sorting was lazy, it didn’t change the fundamental principle of streams that each item is streamed to the terminal operation at most once.
You said that you expected the sorting “to be lazy” but what you actually expected is the sorting to happen again for each item after consumption which would imply that sorting a stream of n elements means actually sorting n items n times which no-one else would expect, especially as peek
is not meant to have a side effect that affects the ongoing operation.
Do I understand you correctly that you want to take the largest number from the list, decrement it, put it back into the list, repeat 10 times?
If so, then this is a job for PriorityQueue:
PriorityQueue<Integer> queue = Stream.of(2005,2020,2000,1967,2018)
.collect(toCollection(() -> new PriorityQueue(reverseOrder())));
Stream.generate(() -> {
Integer year = queue.poll();
queue.add(year - 1);
return year;
}).limit(10).forEach(System.out::println);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With