Java では、for-each ループによってコレクションまたは配列の走査プロセスが簡素化されますが、すべての Java プログラマがこの記事で説明する for-each ループの詳細を知っているわけではありません。 Java 5 でリリースされた他の用語 (エイリアスリリースされたジェネリックス、自動カプセル化、および可変引数パラメーター) とは異なり、Java 開発者は他の機能よりも for-each ループを頻繁に使用しますが、高度な for-each ループがどのように機能するか、または基本的なものは何かと尋ねると、 for-each ループでコレクションを使用する場合の要件に、誰もが答えられるわけではありません。
このチュートリアルと例は、for-each ループ内のいくつかの興味深いパズルを掘り下げることで、そのギャップを埋めることを目的としています。さて、詳細には立ち入りませんが、Java5 の for-each ループに関する最初の問題を見てみましょう。
上級ループの質問 1
ユーザー定義のアグリゲータまたはコレクション クラスを走査する次のコードを考えてみましょう。このコードでは、例外がスローされるか、コンパイラ エラーがスローされるかにかかわらず、何が出力されるかを考えてください。
次のようにコードをコピーします。
パッケージテスト。
/**
* Java での for-each ループがどのように機能するかを示す Java クラス
*/
パブリック クラス ForEachTest {
public static void main(String args[]){
CustomCollection<String> myCollection = new CustomCollection<String>();
myCollection.add("Java");
myCollection.add("Scala");
myCollection.add("グルービー");
//このコードは何をするのか、言語を出力するか、例外をスローするか、コンパイル時エラーをスローするか
for(文字列言語: myCollection){
System.out.println(言語);
}
}
}
以下は CustomCollection クラスです。これは他の Collection クラスと同様に ArrayList に依存するジェネリック クラスで、Collection に項目を追加および削除するためのメソッドを提供します。
次のようにコードをコピーします。
パッケージテスト。
パブリック クラス CustomCollection<T>{
プライベート ArrayList<T> バケット。
public CustomCollection(){
バケット = 新しい ArrayList();
}
public int size() {
バケット.サイズ()を返します;
}
パブリックブール値 isEmpty() {
戻りバケット.isEmpty();
}
public boolean contains(T o) {
戻りバケット.contains(o);
}
public boolean add(T e) {
戻りバケット.add(e);
}
public boolean Remove(T o) {
戻りバケット.remove(o);
}
}
答え:
CustomCollection クラスが java.lang.Iterable インターフェイスを実装していないため、上記のコードはコンパイルされません。コンパイル時エラーは次のとおりです。
次のようにコードをコピーします。
スレッド「メイン」java.lang.RuntimeException での例外: コンパイルできないソース コード - for-each は式タイプには適用できません
必須: 配列または java.lang.Iterable
見つかりました: test.CustomCollection
test.ForEachTest.main(ForEachTest.java:24) で
ここから学ぶべき興味深い事実は、for-each ループは Iterable インターフェースを実装する Java 配列および Collection クラスにのみ適用されるということです。また、すべての組み込み Collection クラスは java.util.Collection インターフェースを実装し、Iterable を継承しているため、これは見落とされがちな詳細は、Collection インターフェイスの型宣言「パブリック インターフェイス Collection extends Iterable」に見ることができます。したがって、上記の問題を解決するには、単純に CustomCollection に Collection インターフェイスを実装させるか、AbstractCollection を継承するかを選択できます。これは、デフォルトのユニバーサル実装であり、柔軟性を高めるために抽象クラスとインターフェイスを同時に使用する方法を示しています。次に、for-each ループの 2 番目のパズルを見てみましょう。
Java for-each ループの 2 番目の困難:
次のコード例では、ConcurrentModificationException がスローされます。ここでは、標準のイテレータと for-each ループを使用して ArrayList を反復処理し、要素を削除します。どのコード部分が ConcurrentModificationException をスローするのか、またその理由を調べる必要があります。答えは両方とも、どちらでもない、またはどちらか一方である可能性があることに注意してください。
次のようにコードをコピーします。
パッケージテスト。
java.util.ArrayListをインポートします。
java.util.Collectionをインポートします。
java.util.Iteratorをインポートします。
/**
* Java の for-each ループの内部動作を示す Java クラス
* @著者ジャビン・ポール
**/
パブリック クラス ForEachTest2 {
public static void main(String args[]){
Collection<String> リスト = new ArrayList<String>();
list.add("Android");
list.add("iPhone");
list.add("Windows モバイル");
// どのコードが ConcurrentModificationException をスローするか、両方
// どれもないか、どれか 1 つ
// 例 1
Iterator<String> itr = list.iterator();
while(itr.hasNext()){
文字列 lang = itr.next();
list.remove(lang);
}
//例2
for(文字列言語: リスト){
list.remove(言語);
}
}
}
Java 開発者の約 70% は、要素の削除にイテレータの Remove メソッドを使用せず、ArrayList の Remove() メソッドを使用するため、最初のコード ブロックで ConcurrentModificationException 例外がスローされると言っています。ただし、ここではイテレータを使用していないため、for-each ループでも同じ問題が発生すると言う Java 開発者は多くありません。実際、2 番目のコード スニペットも ConcurrentModificationException をスローしますが、これは最初の混乱を解決した後で明らかになります。 for-each ループは内部で Iterator を使用して Collection を走査するため、Iterator.next() も呼び出します。これにより (要素の) 変更がチェックされ、ConcurrentModificationException がスローされます。これは、最初のスニペットをコメントアウトした後に 2 番目のスニペットを実行すると得られる以下の出力からわかります。
次のようにコードをコピーします。
スレッド「メイン」での例外 java.util.ConcurrentModificationException
java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372) で
java.util.AbstractList$Itr.next(AbstractList.java:343) で
test.ForEachTest2.main(ForEachTest2.java:34) で
Java5 の for-each ループについては以上です。 Java プログラマーが Collection クラスを反復処理するコードを作成するとき、特に要素を同時に削除しながらコレクションを反復処理するときに、多くの問題が発生することを私たちは見てきました。コレクション (マップ、セット、リストなど) からオブジェクトを削除するときは、必ずイテレーターの削除メソッドを使用してください。また、for-each ループは、 の標準的な使用法に加えて単なる構文糖衣 (構文糖衣) であることにも注意してください。標準の反復子コード (sugar) のみ。
翻訳者注: 構文シュガー (糖衣文法とも訳される) は、英国のコンピューター科学者ピーター J. ランディンによって発明された用語であり、コンピューター言語に追加された特定の種類の文法を指します。この構文は、言語の機能には影響しません。言語ですが、プログラマーにとって使いやすくなります。一般に、シンタックス シュガーを使用するとプログラムの可読性が向上し、プログラム コード エラーの可能性が減ります。