Java 8 forEachのガイド
1. 概要
Java 8で導入されたforEachループは、プログラマーにa new, concise and interesting way for iterating over a collectionを提供します。
この記事では、コレクションでforEachを使用する方法、必要な引数の種類、およびこのループが拡張されたfor-loopとどのように異なるかを説明します。
Java 8のいくつかの概念をブラッシュアップする必要がある場合は、collection of articlesが役立ちます。
2. forEachの基本
Javaでは、CollectionインターフェースのスーパーインターフェースとしてIterableがあります。Java8以降、このインターフェースには新しいAPIがあります。
void forEach(Consumer super T> action)
簡単に言えば、forEachのJavadocは、“performs the given action for each element of the Iterable until all elements have been processed or the action throws an exception.”であることを示します。
したがって、forEachを使用すると、他のIterator.と同様に、コレクションを反復処理して、各要素に対して特定のアクションを実行できます。
たとえば、StringsのCollectionを反復および出力するfor-loopバージョン:
for (String name : names) {
System.out.println(name);
}
これは、forEachを使用して次のように記述できます。
names.forEach(name -> {
System.out.println(name);
});
3. forEachメソッドの使用
forEachを使用してコレクションを反復処理し、各要素に対して特定のアクションを実行します。 The action to be performed is contained in a class that implements the Consumer interface and is passed to forEach as an argument.
Consumerインターフェースはa functional interface(単一の抽象メソッドを持つインターフェース)です。 入力を受け入れ、結果を返しません。
定義は次のとおりです。
@FunctionalInterface
public interface Consumer {
void accept(T t);
}
したがって、実装、たとえば、Stringを単に出力するコンシューマー:
Consumer printConsumer = new Consumer() {
public void accept(String name) {
System.out.println(name);
};
};
引数としてforEachに渡すことができます:
names.forEach(printConsumer);
ただし、コンシューマーを介してアクションを作成し、forEachAPIを使用する方法はこれだけではありません。
forEachメソッドを使用する最も一般的な3つの方法を見てみましょう。
3.1. 匿名のConsumerの実装
匿名クラスを使用してConsumerインターフェイスの実装をインスタンス化し、それを引数としてforEachメソッドに適用できます。
Consumer printConsumer= new Consumer() {
public void accept(String name) {
System.out.println(name);
}
};
names.forEach(printConsumer);
これはうまく機能しますが、上記の例で分析すると、実際に使用されている部分はaccept()メソッド内のコードであることがわかります。
Lambda式がこれを行うための標準的で簡単な方法になりましたが、Consumerインターフェースを実装する方法を知ることは依然として価値があります。
3.2. ラムダ式
Java 8機能インターフェイスの主な利点は、Lambda式を使用してそれらをインスタンス化し、巨大な匿名クラス実装の使用を回避できることです。
Consumerインターフェースは関数型インターフェースであるため、Lambdaで次の形式で表現できます。
(argument) -> { //body }
したがって、printConsumerは次のように簡略化されます。
name -> System.out.println(name)
そして、それをforEach asに渡すことができます。
names.forEach(name -> System.out.println(name));
Java 8でLambda式が導入されて以来、これはおそらくforEachメソッドを使用する最も一般的な方法です。
ラムダには非常に現実的な学習曲線があるため、始めたばかりの場合は、this write-upが新しい言語機能を使用するためのいくつかの優れた方法を検討します。
3.3. メソッドリファレンス
クラスで操作を実行するためのメソッドが既に存在する通常のLambda構文の代わりに、メソッド参照構文を使用できます。
names.forEach(System.out::println);
4. forEachの操作
4.1. Collectionを反復処理
タイプCollection – list, set, queue etcの反復可能オブジェクト。 forEach.を使用するための同じ構文を持っている
したがって、すでに見たように、リストの要素を繰り返すには:
List names = Arrays.asList("Larry", "Steve", "James");
names.forEach(System.out::println);
同様にセットの場合:
Set uniqueNames = new HashSet<>(Arrays.asList("Larry", "Steve", "James"));
uniqueNames.forEach(System.out::println);
または、CollectionでもあるQueueについて考えてみましょう。
Queue namesQueue = new ArrayDeque<>(Arrays.asList("Larry", "Steve", "James"));
namesQueue.forEach(System.out::println);
4.2. マップの反復–マップのforEachを使用
マップはIterableではありませんが、provide their own variant of forEach that accepts a*BiConsumer*. を実行します
IterableのforEachにConsumerの代わりにBiConsumerが導入されたため、Mapのキーと値の両方に対して同時にアクションを実行できます。
エントリを持つMapを作成しましょう:
Map namesMap = new HashMap<>();
namesMap.put(1, "Larry");
namesMap.put(2, "Steve");
namesMap.put(3, "James");
次に、マップのforEachを使用して、namesMapを繰り返し処理します。
namesMap.forEach((key, value) -> System.out.println(key + " " + value));
ここでわかるように、BiConsumerを使用しました。
(key, value) -> System.out.println(key + " " + value)
Mapのエントリを反復処理します。
4.3. Map – by iterating entrySetを反復処理
IterableのforEach.を使用して、Map のEntrySet を反復処理することもできます。
the entries of a Map are stored in a Set called EntrySet, we can iterate that using a forEach:以降
namesMap.entrySet().forEach(entry -> System.out.println(
entry.getKey() + " " + entry.getValue()));
5. ForeachとFor-Loop
単純な観点から、両方のループは同じ機能を提供します。コレクション内の要素をループします。
The main difference between the two of them is that they are different iterators – the enhanced for-loop is an external iterator whereas the new forEach method is an internal one。
5.1. 内部イテレータ–forEach
このタイプのイテレーターは、バックグラウンドで反復を管理し、プログラマーがコレクションの要素を使用して何を行うかをコーディングするだけにします。
代わりに、反復子が反復を管理し、要素を1つずつ処理するようにします。
内部イテレータの例を見てみましょう。
names.forEach(name -> System.out.println(name));
上記のforEachメソッドでは、提供された引数がラムダ式であることがわかります。 これは、メソッドがwhat is to be doneを知るだけでよく、反復のすべての作業が内部で処理されることを意味します。
5.2. 外部イテレータ–for-loop
外部イテレータは、ループが実行されるwhatとhowを混合します。
Enumerations、Iterators、および拡張for-loopはすべて外部イテレータです(メソッドiterator(),next()またはhasNext()を覚えていますか? ). これらすべてのイテレータでは、反復の実行方法を指定するのが私たちの仕事です。
この馴染みのあるループを考えてみましょう。
for (String name : names) {
System.out.println(name);
}
リストの反復中にhasNext()またはnext()メソッドを明示的に呼び出すことはありませんが、この反復を機能させる基礎となるコードはこれらのメソッドを使用します。 これは、これらの操作の複雑さはプログラマから隠されているが、まだ存在することを意味します。
コレクションが繰り返しを行う内部イテレータとは異なり、ここではコレクションからすべての要素を取り出す外部コードが必要です。
6. 結論
この記事では、forEachループが通常のfor-loopよりも便利であることを示しました。
また、forEachメソッドがどのように機能するか、およびコレクション内の各要素に対してアクションを実行するために引数としてどのような実装を受け取ることができるかについても説明しました。
最後に、この記事で使用されているすべてのスニペットは、Githubリポジトリで入手できます。