2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Java8のStreamAPIでwhile文と同等の処理を書きたかった話

Last updated at Posted at 2018-01-22

例えば、ある起点日( 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 クラスを作って類似な動きをさせることはできるので自由度が高い開発ではやってみてもいいかも?

2
4
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?