検討してください:
List<String> someList = new ArrayList<String>();
// add "monkey", "donkey", "skeleton key" to someList
for (String item : someList) {
System.out.println(item);
}
for for each構文を使用しないと、同等のfor
ループはどのようになりますか?
for (Iterator<String> i = someIterable.iterator(); i.hasNext();) {
String item = i.next();
System.out.println(item);
}
ループ内でi.remove();
を使用する必要がある場合、または実際のイテレータにアクセスする必要がある場合は、実際のイテレータが推論されるだけなので、for ( : )
イディオムを使用することはできません。
Denis Buenoが指摘したように、このコードは Iterable
インターフェース を実装するすべてのオブジェクトに対して機能します。
また、for (:)
イディオムの右側がarray
オブジェクトではなくIterable
である場合、内部コードはintインデックスカウンタを使用し、代わりにarray.length
に対してチェックします。 Java言語仕様 を参照してください。
構文for eachは配列にも有効です。例えば.
String[] fruits = new String[] { "Orange", "Apple", "Pear", "Strawberry" };
for (String fruit : fruits) {
// fruit is an element of the `fruits` array.
}
これは本質的に
for (int i = 0; i < fruits.length; i++) {
String fruit = fruits[i];
// fruit is an element of the `fruits` array.
}
だから、全体的な要約:
[nsayer] 以下は、何が起こっているのかを示す長い形式です。
for(Iterator<String> i = someList.iterator(); i.hasNext(); ) { String item = i.next(); System.out.println(item); }
I.remove()を使う必要があるならば注意してください。実際のイテレータは単に推論されるだけなので、ループ内で、または何らかの方法で実際のイテレータにアクセスすると、for(:)イディオムは使用できません。
それはnsayerの答えによって暗示されていますが、OPのfor(..)構文が "someList"がJava.lang.Iterableを実装するものであればうまくいくことに注目する価値があります。 Java.utilしたがって、あなた自身の型でさえも、この構文で使用することができます。
Java 5「拡張forループ」とも呼ばれます)に追加された foreach
ループ は、{ Java.util.Iterator
の構文糖を使用するのと同等です。同じこと。したがって、各要素を1つずつ順番に読み込むときは、より便利で簡潔になるように、常にforeach
をイテレータで選択する必要があります。
for(int i : intList) {
System.out.println("An element in the list: " + i);
}
Iterator<Integer> intItr = intList.iterator();
while(intItr.hasNext()) {
System.out.println("An element in the list: " + intItr.next());
}
Iterator
を直接使用しなければならない状況があります。たとえば、foreach
を使用しているときに要素を削除しようとすると、ConcurrentModificationException
が生成される可能性があります。
foreach
とfor
:基本的な違いfor
とforeach
の唯一の実用的な違いは、インデックス可能オブジェクトの場合、インデックスにアクセスできないということです。基本的なfor
ループが必要な場合の例
for(int i = 0; i < array.length; i++) {
if(i < 5) {
// Do something special
} else {
// Do other stuff
}
}
foreach
を使用して手動で別のインデックスint-variableを作成できますが、
int idx = -1;
for(int i : intArray) {
idx++;
...
}
variable-scope は理想的ではなく、基本的なfor
ループは単にこのユースケースの標準的なフォーマットであり、推奨されるフォーマットではありません。
foreach
とfor
:パフォーマンスコレクションにアクセスするとき、foreach
は基本的なfor
ループの配列アクセスよりも かなり速い です。しかし、配列にアクセスするとき、少なくともプリミティブ配列とラッパー配列を使うと、インデックスを介したアクセスは劇的に速くなります。
int
またはInteger
配列にアクセスするとき、インデックスは23 -_(40イテレータよりも高速です。これは、この記事の最後にあるテスト用クラスの出力です。100要素のprimitive-int配列の数の合計です。イテレータ、Bはインデックス)
[C:\Java_code\]Java TimeIteratorVsIndexIntArray 1000000
Test A: 358,597,622 nanoseconds
Test B: 269,167,681 nanoseconds
B faster by 89,429,941 nanoseconds (24.438799231635727% faster)
[C:\Java_code\]Java TimeIteratorVsIndexIntArray 1000000
Test A: 377,461,823 nanoseconds
Test B: 278,694,271 nanoseconds
B faster by 98,767,552 nanoseconds (25.666236154695838% faster)
[C:\Java_code\]Java TimeIteratorVsIndexIntArray 1000000
Test A: 288,953,495 nanoseconds
Test B: 207,050,523 nanoseconds
B faster by 81,902,972 nanoseconds (27.844689860906513% faster)
[C:\Java_code\]Java TimeIteratorVsIndexIntArray 1000000
Test A: 375,373,765 nanoseconds
Test B: 283,813,875 nanoseconds
B faster by 91,559,890 nanoseconds (23.891659337194227% faster)
[C:\Java_code\]Java TimeIteratorVsIndexIntArray 1000000
Test A: 375,790,818 nanoseconds
Test B: 220,770,915 nanoseconds
B faster by 155,019,903 nanoseconds (40.75164734599769% faster)
[C:\Java_code\]Java TimeIteratorVsIndexIntArray 1000000
Test A: 326,373,762 nanoseconds
Test B: 202,555,566 nanoseconds
B faster by 123,818,196 nanoseconds (37.437545972215744% faster)
私はInteger
配列についてもこれを実行しました、そしてインデックスはまだ明らかな勝者です、しかし18から25パーセントだけ速いです。
ただし、List
がIntegers
の場合、反復子は明らかに勝者です。 test-classのint-arrayを以下のように変更するだけです。
List<Integer> intList = Arrays.asList(new Integer[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100});
そしてtest-functionに必要な変更を加えます(int[]
からList<Integer>
へ、length
からsize()
へ、など)。
[C:\Java_code\]Java TimeIteratorVsIndexIntegerList 1000000
Test A: 3,429,929,976 nanoseconds
Test B: 5,262,782,488 nanoseconds
A faster by 1,832,852,512 nanoseconds (34.326681820485675% faster)
[C:\Java_code\]Java TimeIteratorVsIndexIntegerList 1000000
Test A: 2,907,391,427 nanoseconds
Test B: 3,957,718,459 nanoseconds
A faster by 1,050,327,032 nanoseconds (26.038700083921256% faster)
[C:\Java_code\]Java TimeIteratorVsIndexIntegerList 1000000
Test A: 2,566,004,688 nanoseconds
Test B: 4,221,746,521 nanoseconds
A faster by 1,655,741,833 nanoseconds (38.71935684115413% faster)
[C:\Java_code\]Java TimeIteratorVsIndexIntegerList 1000000
Test A: 2,770,945,276 nanoseconds
Test B: 3,829,077,158 nanoseconds
A faster by 1,058,131,882 nanoseconds (27.134122749113843% faster)
[C:\Java_code\]Java TimeIteratorVsIndexIntegerList 1000000
Test A: 3,467,474,055 nanoseconds
Test B: 5,183,149,104 nanoseconds
A faster by 1,715,675,049 nanoseconds (32.60101667104192% faster)
[C:\Java_code\]Java TimeIteratorVsIndexIntList 1000000
Test A: 3,439,983,933 nanoseconds
Test B: 3,509,530,312 nanoseconds
A faster by 69,546,379 nanoseconds (1.4816434912159906% faster)
[C:\Java_code\]Java TimeIteratorVsIndexIntList 1000000
Test A: 3,451,101,466 nanoseconds
Test B: 5,057,979,210 nanoseconds
A faster by 1,606,877,744 nanoseconds (31.269164666060377% faster)
あるテストでは、それらはほぼ同等ですが、コレクションでは、イテレータが勝ちます。
*この記事は、私がStack Overflowについて書いた2つの答えに基づいています。
さらに詳しい情報:どちらがより効率的か、FOR-EACHループか、イテレータか?
私はStack Overflowで この質問 を読んだ後に、この2つのことを行うための時間比較クラスを作成しました。
import Java.text.NumberFormat;
import Java.util.Locale;
/**
<P>{@code Java TimeIteratorVsIndexIntArray 1000000}</P>
@see <CODE><A HREF="https://stackoverflow.com/questions/180158/how-do-i-time-a-methods-execution-in-Java">https://stackoverflow.com/questions/180158/how-do-i-time-a-methods-execution-in-Java</A></CODE>
**/
public class TimeIteratorVsIndexIntArray {
public static final NumberFormat nf = NumberFormat.getNumberInstance(Locale.US);
public static final void main(String[] tryCount_inParamIdx0) {
int testCount;
// Get try-count from a command-line parameter
try {
testCount = Integer.parseInt(tryCount_inParamIdx0[0]);
}
catch(ArrayIndexOutOfBoundsException | NumberFormatException x) {
throw new IllegalArgumentException("Missing or invalid command line parameter: The number of testCount for each test. " + x);
}
//Test proper...START
int[] intArray = new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100};
long lStart = System.nanoTime();
for(int i = 0; i < testCount; i++) {
testIterator(intArray);
}
long lADuration = outputGetNanoDuration("A", lStart);
lStart = System.nanoTime();
for(int i = 0; i < testCount; i++) {
testFor(intArray);
}
long lBDuration = outputGetNanoDuration("B", lStart);
outputGetABTestNanoDifference(lADuration, lBDuration, "A", "B");
}
private static final void testIterator(int[] int_array) {
int total = 0;
for(int i = 0; i < int_array.length; i++) {
total += int_array[i];
}
}
private static final void testFor(int[] int_array) {
int total = 0;
for(int i : int_array) {
total += i;
}
}
//Test proper...END
//Timer testing utilities...START
public static final long outputGetNanoDuration(String s_testName, long l_nanoStart) {
long lDuration = System.nanoTime() - l_nanoStart;
System.out.println("Test " + s_testName + ": " + nf.format(lDuration) + " nanoseconds");
return lDuration;
}
public static final long outputGetABTestNanoDifference(long l_aDuration, long l_bDuration, String s_aTestName, String s_bTestName) {
long lDiff = -1;
double dPct = -1.0;
String sFaster = null;
if(l_aDuration > l_bDuration) {
lDiff = l_aDuration - l_bDuration;
dPct = 100.00 - (l_bDuration * 100.0 / l_aDuration + 0.5);
sFaster = "B";
}
else {
lDiff = l_bDuration - l_aDuration;
dPct = 100.00 - (l_aDuration * 100.0 / l_bDuration + 0.5);
sFaster = "A";
}
System.out.println(sFaster + " faster by " + nf.format(lDiff) + " nanoseconds (" + dPct + "% faster)");
return lDiff;
}
//Timer testing utilities...END
}
これはJavaイテレータの知識を前提としない答えです。精度は劣りますが、教育には役立ちます。
プログラミング中に、私たちはしばしば次のようなコードを書く。
char[] grades = ....
for(int i = 0; i < grades.length; i++) { // for i goes from 0 to grades.length
System.out.print(grades[i]); // Print grades[i]
}
Foreach構文は、この共通パターンをより自然で構文的にノイズの少ない方法で書くことを可能にします。
for(char grade : grades) { // foreach grade in grades
System.out.print(grade); // print that grade
}
さらに、この構文は、配列インデックスをサポートしていないがJava Iterableインタフェースを実装しているListsやSetsなどのオブジェクトにも有効です。
Javaのfor-eachループは、基礎となるイテレータメカニズムを使用します。それで、それは以下と同じです:
Iterator<String> iterator = someList.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
System.out.println(item);
}
Java 8の機能では、これを使うことができます。
List<String> messages = Arrays.asList("First", "Second", "Third");
void forTest(){
messages.forEach(System.out::println);
}
First
Second
Third
それはnsayerの答えによって暗示されていますが、OPのfor(..)構文が "someList"が なんでも - Java.lang.Iterableを実装するものであるときにうまくいくことは注目に値する、リストである必要はない、またはJava.utilからのコレクション。したがって、あなた自身の型でさえも、この構文で使用することができます。
Foreachループの構文は次のとおりです。
for (type obj:array) {...}
例:
String[] s = {"Java", "Coffe", "Is", "Cool"};
for (String str:s /*s is the array*/) {
System.out.println(str);
}
出力:
Java
Coffe
Is
Cool
警告:foreachループを使って配列要素にアクセスすることはできますが、それらを初期化することはできません。そのためにはオリジナルのfor
ループを使用してください。
警告:あなたは他のオブジェクトと配列の型を一致させる必要があります。
for (double b:s) // Invalid-double is not String
要素を編集したい場合は、次のように元のfor
ループを使用してください。
for (int i = 0; i < s.length-1 /*-1 because of the 0 index */; i++) {
if (i==1) //1 because once again I say the 0 index
s[i]="2 is cool";
else
s[i] = "hello";
}
Sをコンソールにダンプすると、次のようになります。
hello
2 is cool
hello
hello
Javaの "for-each"ループ構造は、2種類のオブジェクトに対する繰り返しを可能にします。
T[]
(あらゆるタイプの配列) Java.lang.Iterable<T>
Iterable<T>
インターフェースにはIterator<T> iterator()
という1つのメソッドしかありません。 Collection<T>
インターフェースはCollection<T>
を拡張するため、これはIterable<T>
型のオブジェクトに対して機能します。
_ jls _ for-eachループで定義されているように、2つの形式があります。
Expressionの型がIterable
のサブタイプである場合、変換は次のようになります。
List<String> someList = new ArrayList<String>();
someList.add("Apple");
someList.add("Ball");
for (String item : someList) {
System.out.println(item);
}
// IS TRANSLATED TO:
for(Iterator<String> stringIterator = someList.iterator(); stringIterator.hasNext(); ) {
String item = stringIterator.next();
System.out.println(item);
}
Expressionの配列型がT[]
である必要がある場合は、次のようにします。
String[] someArray = new String[2];
someArray[0] = "Apple";
someArray[1] = "Ball";
for(String item2 : someArray) {
System.out.println(item2);
}
// IS TRANSLATED TO:
for (int i = 0; i < someArray.length; i++) {
String item2 = someArray[i];
System.out.println(item2);
}
Java 8では、一般的にパフォーマンスの高いストリームが導入されました。それらを次のように使用できます。
someList.stream().forEach(System.out::println);
Arrays.stream(someArray).forEach(System.out::println);
ウィキペディアに記載されているforeachループの概念を以下に強調します。
ただし、他のforループ構成体とは異なり、foreachループは通常 明示的なカウンターなし を維持します。これにより、潜在的な by-by-oneエラー が回避され、コードが読みやすくなります。
そのため、foreachループの概念では、ループは明示的なカウンタを使用しないことを説明しています。つまり、リスト内をトラバースするためにインデックスを使用する必要はないということです。このoff-by-oneエラーの一般的な概念を説明するために、インデックスを使用してリスト内を移動するループの例を見てみましょう。
// In this loop it is assumed that the list starts with index 0
for(int i=0; i<list.length; i++){
}
しかし、リストがインデックス1で始まる場合、このループはインデックス0に要素が見つからないので例外をスローし、このエラーは1行1エラーと呼ばれます。そのため、このような1対1のエラーを回避するために、foreachループの概念が使用されています。他にも利点があるかもしれませんが、これがforeachループを使用することの主な概念と利点であると私は思います。
また、元の質問で "foreach"メソッドを使用すると、反復中にリストから項目を削除できないなど、いくつかの制限があります。
新しいforループは読みやすく、個別のイテレータが不要になりますが、読み取り専用の繰り返しパスでのみ実際に使用可能です。
Java 7
を含む古いJavaバージョンを使用すると、以下のようにforeach
ループを使用できます。
List<String> items = new ArrayList<>();
items.add("A");
items.add("B");
items.add("C");
items.add("D");
items.add("E");
for(String item : items){
System.out.println(item);
}
以下はJava 8
でforeach
ループを使用するための最新の方法です。
(forEach
+ lambda式またはメソッド参照を使ってListをループします)
//lambda
//Output : A,B,C,D,E
items.forEach(item->System.out.println(item));
//method reference
//Output : A,B,C,D,E
items.forEach(System.out::println);
詳細についてはこのリンクを参照してください。
これは等価な式です。
for(Iterator<String> sit = someList.iterator(); sit.hasNext(); ) {
System.out.println(sit.next());
}
あなたの "for each"を避けるためにforEachに代わるものがあります。
List<String> someList = new ArrayList<String>();
変形1(プレーン):
someList.stream().forEach(listItem -> {
System.out.println(listItem);
});
バリエーション2(並列実行(高速)):
someList.parallelStream().forEach(listItem -> {
System.out.println(listItem);
});
それはすべての基本的なループクラッタを取り除くことによってあなたのコードに美しさを追加します。それはあなたのコードをきれいに見せます。
通常のfor
ループ:
void cancelAll(Collection<TimerTask> list) {
for (Iterator<TimerTask> i = list.iterator(); i.hasNext();)
i.next().cancel();
}
for-eachを使用します。
void cancelAll(Collection<TimerTask> list) {
for (TimerTask t : list)
t.cancel();
}
for-each は、 Iterator を実装したコレクションを構成するものです。忘れないでください、あなたのコレクションは Iterator を実装するべきです。そうでなければfor-eachと一緒に使うことはできません。
次の行は、リスト内の各TimerTask tに対して、 " "と読み取られます。 "
for (TimerTask t : list)
For-eachの場合にエラーが発生する可能性は少なくなります。イテレータを初期化したり、ループカウンタを初期化して終了させることを心配する必要はありません(エラーの範囲がある場合)。
それはこのように見えるでしょう。とても辛い。
for (Iterator<String> i = someList.iterator(); i.hasNext(); )
System.out.println(i.next());
Sunのドキュメント のfor eachについての良い記事があります。
Java 8より前のバージョンでは、次のものを使用する必要があります。
Iterator<String> iterator = someList.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
System.out.println(item);
}
ただし、Java 8でStreamsが導入されたことで、はるかに少ない構文で同じことを実行できます。例えば、あなたのsomeList
に対しては、次のことができます。
someList.stream().forEach(System.out::println);
ストリームについてもっと知ることができます ここ 。
多くの良い答えが言ったように、オブジェクトがIterable interface
ループを使用したい場合、オブジェクトはfor-each
を実装しなければなりません。
簡単な例をポストして、for-each
ループがどのように機能するかを別の方法で説明しようとします。
for-each
ループの例:
public class ForEachTest {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("111");
list.add("222");
for (String str : list) {
System.out.println(str);
}
}
}
次に、このクラスを逆コンパイルするためにjavap
を使用すると、このバイトコードのサンプルが得られます。
public static void main(Java.lang.String[]);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=4, args_size=1
0: new #16 // class Java/util/ArrayList
3: dup
4: invokespecial #18 // Method Java/util/ArrayList."<init>":()V
7: astore_1
8: aload_1
9: ldc #19 // String 111
11: invokeinterface #21, 2 // InterfaceMethod Java/util/List.add:(Ljava/lang/Object;)Z
16: pop
17: aload_1
18: ldc #27 // String 222
20: invokeinterface #21, 2 // InterfaceMethod Java/util/List.add:(Ljava/lang/Object;)Z
25: pop
26: aload_1
27: invokeinterface #29, 1 // InterfaceMethod Java/util/List.iterator:()Ljava/util/Iterator;
サンプルの最後の行からわかるように、コンパイラーはコンパイル時にfor-each
キーワードの使用をIterator
の使用に自動的に変換します。 Iterable interface
を実装していないobjectがfor-each
ループを使おうとするとException
を投げるのはなぜでしょうか。
Java 8では、彼らはforEachを導入しました。リストを使用して、地図をループすることができます。
forを使ってリストをループする
List<String> someList = new ArrayList<String>();
someList.add("A");
someList.add("B");
someList.add("C");
someList.forEach(listItem -> System.out.println(listItem))
または
someList.forEach(listItem-> {
System.out.println(listItem);
});
forを使ってマップをループする
Map<String, String> mapList = new HashMap<>();
mapList.put("Key1", "Value1");
mapList.put("Key2", "Value2");
mapList.put("Key3", "Value3");
mapList.forEach((key,value)->System.out.println("Key: " + key + " Value : " + value));
または
mapList.forEach((key,value)->{
System.out.println("Key : " + key + " Value : " + value);
});
public static Boolean Add_Tag(int totalsize)
{ List<String> fullst = new ArrayList<String>();
for(int k=0;k<totalsize;k++)
{
fullst.addAll();
}
}
各ループのJava (拡張forループとも呼ばれる)は、forループの簡易版です。利点は、記述するコードが少なく、管理する変数が少ないことです。欠点は、ステップ値を制御できず、ループ本体内のループインデックスにアクセスできないということです。
Step値が単純な1の増分であるとき、そして現在のloop要素へのアクセスだけが必要なときに、これらは最もよく使われます。たとえば、現在の要素の前後を見ずに、配列またはコレクション内のすべての要素をループ処理する必要がある場合です。
ループ初期化もブール条件もなく、step値は暗黙的で単純な増分です。これが、通常のforループよりもはるかに単純であると見なされる理由です。
拡張forループはこの実行順序に従います。
1)ループ本体
2)アレイまたはコレクション全体がトラバースされるまで、ステップ1から繰り返します。
例 - 整数配列
int [] intArray = {1, 3, 5, 7, 9};
for(int currentValue : intArray) {
System.out.println(currentValue);
}
CurrentValue変数は、intArray配列でループされている現在の値を保持します。明確なステップ値がないことに注意してください - それは常に1ずつ増加します。
コロンは「in」を意味すると考えることができます。そのため、拡張forループ宣言では、intArrayをループして現在の配列を格納し、int値in currentValue変数を格納します。
出力:
1
3
5
7
9
例 - 文字配列
For-eachループを使用して文字列の配列を反復処理できます。ループ宣言は次のように述べています。myStrings文字列配列をループし、現在の文字列値in currentString変数を格納します。
String [] myStrings = {
"alpha",
"beta",
"gamma",
"delta"
};
for(String currentString : myStrings) {
System.out.println(currentString);
}
出力:
alpha
beta
gamma
delta
例 - リスト
次のように、拡張forループを使用してJava.util.Listを反復処理することもできます。
List<String> myList = new ArrayList<String>();
myList.add("alpha");
myList.add("beta");
myList.add("gamma");
myList.add("delta");
for(String currentItem : myList) {
System.out.println(currentItem);
}
ループ宣言では、myList List of Stringsをループ処理し、現在のList値in currentItem変数を格納します。
出力:
alpha
beta
gamma
delta
例 - 設定
次のように、拡張forループを使用してJava.util.Setを反復処理することもできます。
Set<String> mySet = new HashSet<String>();
mySet.add("alpha");
mySet.add("alpha");
mySet.add("beta");
mySet.add("gamma");
mySet.add("gamma");
mySet.add("delta");
for(String currentItem : mySet) {
System.out.println(currentItem);
}
ループ宣言は次のように述べています。mySet Set of Stringsをループし、現在のSet値in currentItem変数を格納します。これはSetなので、重複したString値は格納されません。
出力:
alpha
delta
beta
gamma
Java for-eachイディオムは、 * Iterable 型の配列またはオブジェクトにのみ適用できます。この慣用句は、 暗黙の であり、実際にはイテレーターによってサポートされています。イテレーターはプログラマーによってプログラムされ、その位置を追跡するために整数インデックスまたはノード(データ構造に応じて)を使用することがよくあります。紙上では、通常のforループよりも遅く、配列やリストのような「線形」構造には遅くなりますが、より抽象化されています。
これはクレイジーに見えますが、ちょっとそれがうまくいく
List<String> someList = new ArrayList<>(); //has content
someList.forEach(System.out::println);
これはうまくいきます。 マジック