Java:Stream事例集

2019年2月12日



Streamを使っていろいろやりたいのだが、やり方がわからず四苦八苦することがある。
ここでは、ちょっとした事例を集める。

※直接関係は無いが、Stream中で検査例外(チェック例外)が発生する場合には、LambdaExceptionUtilの使い方の使用を考慮されたい。

すべてが同じであればそれを返し、そうでなければ空を返す。

文字列配列が与えられ、その文字列がすべて同じであればそれをOptionalで返し、配列が0個、あるいは一つでも違うものがあればOptional.empty()を返す。

Optional<String>allTheSame(String...strings) {
    List<String>list = Arrays.stream(strings).distinct().limit(2).collect(Collectors.toList());
    if (list.size() == 1) return Optional.of(list.get(0));
    return Optional.empty();
  }

distinct()はequals()比較によって同一性を判断する。limit(2)は、二つ異なるものが出てきたらそれ以上検査しても意味が無いので打ち切る。

いったんリストにしなければならないところがダサいのだが、他のやり方はあるのだろうか?

以下を参考にした。

Optionalにマップする

要素をOptionalにマップして、empty()の場合は除去したい。冷静に考えれば簡単なことなのだが、しばし悩んだ。以下では偶数の時にだけ、それを文字列化したものが返される。

  public void test() {
    Integer[]ints = { 1, 2, 3, 4, 5 };
    Arrays.stream(ints)
      .map(i->toStr(i))
      .filter(opt->opt.isPresent())
      .map(opt->opt.get())
      .forEach(System.out::println);
  }

  // 偶数のときにだけ、それを文字列として返す。
  private Optional<String>toStr(int i) {
    if ((i & 1) == 0) return Optional.of("" + i);
    return Optional.empty();
  }  

結果は以下だ。

2
4

メソッドリファレンスを使って、以下のようにしてもよい。

  public void test() {
    Integer[]ints = { 1, 2, 3, 4, 5 };
    Arrays.stream(ints)
      .map(i->toStr(i))
      .filter(Optional::isPresent)
      .map(Optional::get)
      .forEach(System.out::println);
  }

Filtering a Stream of Optionals in Javaによれば、以下のようにも書ける。

  public void test() {
    Integer[]ints = { 1, 2, 3, 4, 5 };
    Arrays.stream(ints)
      .map(i->toStr(i))
      .flatMap(optStr->optStr.map(Stream::of).orElseGet(Stream::empty))
      .forEach(System.out::println);
  }

当然、以下でもよい。

  public void test() {
    Integer[]ints = { 1, 2, 3, 4, 5 };
    Arrays.stream(ints)
      .map(i->toStr(i))
      .flatMap(optStr->optStr.map(Stream::of).orElse(Stream.empty()))
      .forEach(System.out::println);
  }

Java9のOptionalでは以下のように書ける。

  public void test() {
    Integer[]ints = { 1, 2, 3, 4, 5 };
    Arrays.stream(ints)
      .map(i->toStr(i))
      .flatMap(Optional::stream)
      .forEach(System.out::println);
  }

何のことは無い、Optional#stream()が以下の定義になっている。

  public Stream<T> stream() {
    if (!isPresent()) {
      return Stream.empty();
    } else {
      return Stream.of(value);
    }
  }