7
7

More than 1 year has passed since last update.

【Java】StreamAPIと拡張for文の違い・使い分け

Last updated at Posted at 2021-12-28

現在、参画しているプロジェクトではjavaを用いて開発しています。
その際、StreamAPIと拡張for文を使う場面が多く、それぞれの違いや、場面によっての使い分けを例をあげてまとめてみました。

Stream API とは

Stream APIとはJava SE 8から追加されたイテレーションの拡張APIです。
ListなどのCollectionを扱う為のもので、値の集計やデータを使った処理などが出来る便利なAPIです。

基本的な流れ
1. コレクションからstreamを取得
2. streamに対して「中間操作」を実行
3. 「終端操作」で変換したコレクションの中身に対して処理を適用

中間操作の例

メソッド 内容
filter() ( )内の式がtrueの要素だけを集める
map() 要素を変換
distinct() ストリーム内の重複要素を削除
sorted() Comparableの実装クラスに対する、自然順序でのソート

終端操作の例

メソッド 戻り値型 内容
forEach(Consumer action) void 各要素に対してactionを実行。順序を保証しない
collect(Supplier factory, BiConsumer acc, BiConsumer combiner) 結果コンテナ factoryで生成した可変コンテナに対し、accで要素を追加し、combinerで各コンテナを結合
count() long ストリーム内の重複要素を削除
findFirst() Optional 最初の要素を返す。順番を保持しないストリームでは任意の要素を返却
findAny() Optional 任意の要素を返却

collectの一例として、中間操作された要素をリストにして返すcollect(Collectors.toList())などがある。

StreamAPIと拡張for文を使った例

サンプル:「name(名前)とage(年齢)をフィールドに持つPersonクラス」

Person.java
public class Person {
  private String  name;
  private int     age;

  public String  getName()  { return this.name; }
  public int     getAge()   { return this.age; }

  /* コンストラクタやsetterは省略 */

■Personクラスのリストから年齢が20歳以上の人の、名前のリストを返却するメソッドを各構文で比較

●拡張for文を使用

  public List<String> getAdultPersonNameList(List<Person> personList) {
    List<String> adultPersonNameList = new ArrayList<>();
    for (Person person : personList) {
      if (person.getAge() >= 20) {
       adultPersonNameList.add(person.getName());
      }
    }
    return adultPersonNameList;
  }

●StreamAPIを使用

 public List<String> getAdultPersonNameList(List<Person> personList) {
    return personList.stream() // streamの取得
                     .filter(person -> person.getAge() >= 20) // 中間操作(filter)
                     .map(Person::getName) // 中間操作(map)
                     .collect(Collectors.toList()); //終端操作(collect)
  }

拡張for文を使用するパターンでは「20歳以上の人であればリストに追加」という構造になっています。
StreamAPIを使用するパターンでは「20歳以上の人を集める」→「集めた要素をそれぞれの名前に変換」→「リストにして返却」という構造になっています。

StreamAPIを使用することで可読性が上がるほか、部品性やコードの抽象度も高まっています。
また、中間操作は繋げて書くことができるため、より複雑な操作を行う際に、さらにその効果は発揮されそうです。

StreamAPIが使えないパターン

Streamが使えないパターンというのも残念ながら存在しています。
Streamの処理は一般的なforループとは違って、continueやbreak、returnなどで一部の処理をスキップしたり途中で処理を止めることができません。基本的にStreamは全要素に対して処理をすることを前提にして設計されたからです。
使えないパターンではfor文で処理しましょう。

実際のパターンは以下の通りです。

●複数の変数をまとめて扱いたい場合

1つのCollectionを扱うStreamはこの操作が苦手です。
複数の変数をStreamで扱おうとしてデータを作成するぐらいなら素直にfor文で処理させた方が良いことも多いです。
現在、私が参画しているプロジェクトでもこの場面は非常に多いです。

●CollectionにIndexが欲しい場合

残念ながらJavaにおいて、Stream操作内でIndexを付与して処理されることは出来ません。
ランキング処理のような順位を付けたい場合など、Indexを付与したい場面ではfor文で処理しましょう。

●検査例外を扱いたい場合

StreamというよりはJavaのラムダ式の制限になります。
ラムダ内で発生した検査例外についてはラムダ内部で処理する必要があります。
catchして無理矢理どうにかするという方式をとれば使えなくないのですが、あまりクールではありません。
以下の記事が分かりやすいので、気になる方は見てみてください。
[Java] Streamと例外処理は相性が悪いという話

7
7
0

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
7
7