例えば、ある起点日( startYmd
)から5日ずつ日付を進めていって、Todayを超えたタイミングの日付を求めるような場合、
LocalDate ymd; // 起点日
while (ymd.isBefore(LocalDate.now())) {
ymd = ymd.plusDays(5);
}
System.out.println(ymd); // 起点日から5日ずつ進めてToday以降になる最初の日
と書きますよね。
なんとなく最近 while文
って書かないなーって思って StreamAPI
で書こうと思ったわけです。
あ、Java9に Stream#takeWhile
があるのはわかったうえでJava8で、といった話です。
で、できたのがこのコード。
LocalDate startYmd; // 起点日
Stream.iterate(startYmd, ymd -> ymd.plusDays(5))
.filter(ymd -> !ymd.isBefore(LocalDate.now()))
// .sorted() ← これがあると無限ループ!!
.findFirst()
.ifPresent(ymd -> {
System.out.println(ymd); // 起点日から5日ずつ進めてToday以降になる最初の日
});
コメントを頂いてからだいぶ経ってしまいましたが修正しました。
ステートフルな中間操作である sort()
が無限ループの原因だったようで、当時は勉強不足甚だしい状態でした…
(ちなみに filter()
の評価も逆でしたよね…orz)
はい。無限ループです。
無限Stream VS Stream#filter
という無謀な戦いを挑んだ僕がバカでした。
無限ループを回避しようとすると
LocalDate startYmd; // 起点日
Stream.iterate(startYmd, ymd -> ymd.plusDays(5))
.limit(ChronoUnit.DAYS.between(startYmd, LocalDate.now())) // ÷5+1してもいいけど...
.filter(ymd -> ymd.isBefore(LocalDate.now()))
.sorted()
.findFirst()
.ifPresent(ymd -> {
System.out.println(ymd); // 起点日から5日ずつ進めてToday以降になる最初の日
});
みたいな感じになり、本来の目的は何なのか?なコードになってしまいます。
結論
Java8 では while文
を使え!!
やっぱり StreamAPI
って使ってて楽しいよね!!
蛇足
ちなみに、 Stream#takeWhile
的な動きを実現してくれるライブラリはあるのでそれを使ったり、 TakeWhile
クラスを作って類似な動きをさせることはできるので自由度が高い開発ではやってみてもいいかも?