这取决于您是需要处理所有日子的对象还是某一天的对象。
基于 DiabolicWords 的回答,这是一个处理所有日子的示例:
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);
这将收集一天的对象,处理它们,重置收集并在第二天继续。
如果您只想要某一天:
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;
});
过滤器将跳过specificDay之前的对象,而anyMatch将终止specificDay之后的流。
我已经读到在 Java 9 的流上会有类似 skipWhile 或 takeWhile 的方法。这些会让这变得容易得多。
在 Op 指定目标后编辑更详细
哇,这是一个很好的练习,而且很难破解。问题是一个明显的解决方案(收集流)总是贯穿整个流。您不能获取下一个 x 元素,对它们进行排序,对它们进行流式传输,然后在不一次对整个流(即所有天)执行此操作的情况下重复。出于同样的原因,在流上调用sorted() 将完全通过它(特别是当流不知道元素已经按天排序的事实时)。作为参考,请在此处阅读此评论:https://stackoverflow.com/a/27595803/7653073。
正如他们推荐的那样,这是一个包裹在流中的迭代器实现,它在原始流中向前看,获取一天的元素,对它们进行排序,然后在一个漂亮的新流中为您提供整个内容(不保留记忆中的所有日子!)。实现更加复杂,因为我们没有固定的块大小,但总是必须找到第二天的第一个元素才能知道何时停止。
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);
}
}
}
像这样使用它:
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
说明:
currentDay 和 next 是迭代器的基础,而 firstOfNextDay 和 nextDaysObjects 已经查看了第二天的第一个元素。当currentDay 用尽时,调用nextDay() 并继续将incoming 的元素添加到nextDaysObjects 直到到达第二天,然后将nextDaysObjects 转换为currentDay。
一件事:如果传入的流是 null 或空的,它就会失败。你可以测试null,但是空的情况需要在工厂方法中捕获一个异常。为了便于阅读,我不想添加它。
我希望这是您需要的,请告诉我进展如何。