It depends on whether you want to process objects of all days or one specific day.
Based on DiabolicWords answer, this is an example of processing all days:
TreeSet<MyObject> currentDaysObjects = new TreeSet<>(Comparator.comparing(MyObject::getTimestamp)); LocalDate[] currentDay = new LocalDate[1]; incoming.peek(o -> { LocalDate date = o.getTimestamp().toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); if (!date.equals(currentDay[0])) { if (currentDay != null) { processOneDaysObjects(currentDaysObjects); currentDaysObjects.clear(); } currentDay[0] = date; } }).forEach(currentDaysObjects::add);
This will collect objects in one day, process them, reset the collection and continue from the next day.
If you need only one specific day:
TreeSet<MyObject> currentDaysObjects = new TreeSet<>(Comparator.comparing(MyObject::getTimestamp)); LocalDate specificDay = LocalDate.now(); incoming.filter(o -> !o.getTimestamp() .toInstant() .atZone(ZoneId.systemDefault()) .toLocalDate() .isBefore(specificDay)) .peek(o -> currentDaysObjects.add(o)) .anyMatch(o -> { if (o.getTimestamp().toInstant().atZone(ZoneId.systemDefault()).toLocalDate().isAfter(specificDay)) { currentDaysObjects.remove(o); return true; } return false; });
The filter passes objects to specificDay , and anyMatch will terminate the stream after specificDay .
I read that there will be methods like skipWhile or takeWhile in streams with Java 9. This will simplify this.
Change for a given goal Op
Wow, this is a great exercise, and a pretty tough nut to crack. The problem is that the obvious solution (stream collection) always goes through the whole stream. You cannot take the following x-elements, arrange them, sink them, and then repeat without doing this for the entire stream (i.e., All Days) at once. For the same reason, the call to sorted() in the stream will go through it completely (especially since the stream does not know that the elements are sorted by day already). For reference, read this comment here: fooobar.com/questions/1276164 / ....
As they recommend, here's an Iterator implementation wrapped in a stream that looks forward in the original stream, takes the elements of one day, sorts them and gives you everything in a new new stream (without saving all the days in memory!). The implementation is more complicated because we do not have a fixed block size, but you always need to find the first element of the next day to know when to stop.
public class DayByDayIterator implements Iterator<MyObject> { private Iterator<MyObject> incoming; private MyObject next; private Iterator<MyObject> currentDay; private MyObject firstOfNextDay; private Set<MyObject> nextDaysObjects = new TreeSet<>(Comparator.comparing(MyObject::getTimestamp)); public static Stream<MyObject> streamOf(Stream<MyObject> incoming) { Iterable<MyObject> iterable = () -> new DayByDayIterator(incoming); return StreamSupport.stream(iterable.spliterator(), false); } private DayByDayIterator(Stream<MyObject> stream) { this.incoming = stream.iterator(); firstOfNextDay = incoming.next(); nextDaysObjects.add(firstOfNextDay); next(); } @Override public boolean hasNext() { return next != null; } @Override public MyObject next() { if (currentDay == null || !currentDay.hasNext() && incoming.hasNext()) { nextDay(); } MyObject result = next; if (currentDay != null && currentDay.hasNext()) { this.next = currentDay.next(); } else { this.next = null; } return result; } private void nextDay() { while (incoming.hasNext() && firstOfNextDay.getTimestamp().toLocalDate() .isEqual((firstOfNextDay = incoming.next()).getTimestamp().toLocalDate())) { nextDaysObjects.add(firstOfNextDay); } currentDay = nextDaysObjects.iterator(); if (incoming.hasNext()) { nextDaysObjects = new TreeSet<>(Comparator.comparing(MyObject::getTimestamp)); nextDaysObjects.add(firstOfNextDay); } } }
Use it as follows:
public static void main(String[] args) { Stream<MyObject> stream = Stream.of( new MyObject(LocalDateTime.now().plusHours(1)), new MyObject(LocalDateTime.now()), new MyObject(LocalDateTime.now().plusDays(1).plusHours(2)), new MyObject(LocalDateTime.now().plusDays(1)), new MyObject(LocalDateTime.now().plusDays(1).plusHours(1)), new MyObject(LocalDateTime.now().plusDays(2)), new MyObject(LocalDateTime.now().plusDays(2).plusHours(1))); DayByDayIterator.streamOf(stream).forEach(System.out::println); } ------------------- Output ----------------- 2017-04-30T17:39:46.353 2017-04-30T18:39:46.333 2017-05-01T17:39:46.353 2017-05-01T18:39:46.353 2017-05-01T19:39:46.353 2017-05-02T17:39:46.353 2017-05-02T18:39:46.353
Explanation: currentDay and next are the basis for the iterator, and firstOfNextDay and nextDaysObjects already looking at the first element of the next day. When currentDay exhausted, nextDay() is called and continues to add the incoming element to nextDaysObjects until the next day, and then turns nextDaysObjects into currentDay .
One thing: if the incoming stream is empty or empty, it will fail. You can check for null, but for an empty case, an Exception is required in the factory method. I did not want to add this for readability.
Hope this is what you need, let me know how this happens.