はじめに
普通のfor文より拡張for文の方がIterator使ってるから早いんだぜ!と誰かに教えられたので調べもせずにそのままにしていました。この際ちゃんと調べようと思いました。普通のfor文の方が良いという話になったらどうしようか…
時代はstreamだと思いますが、私の現場はJava6なので後輩にもJava6で書ける書き方を教えなければならないのです。けっしてstreamが書けないからではないのです。
検証環境
- OS Windows10
- Java 11.0.1
- Pleiades All in One Eclipse (2018-12)
書いてみた
検証コード
10万くらいでやったら値が小さすぎて比較できなかったので、1000万回ループさせてみました。
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 10000000; i++) {
list.add(i);
}
//開始
long start = System.currentTimeMillis();
//1000万ループ
for (int i = 0; i < list.size(); i++) {
int tmp = list.get(i);
}
//終了
long end = System.currentTimeMillis();
System.out.println("for文: " + (end - start) + " ms");
//開始
start = System.currentTimeMillis();
//1000万ループ
for (Integer i : list) {
int tmp = i;
}
//終了
end = System.currentTimeMillis();
System.out.println("拡張for文: " + (end - start) + " ms");
}
}
検証結果
そんなばかな…
何回か試したのですが拡張for文が早くなることはなかったです。
for文: 24 ms
拡張for文: 29 ms
検証②
このままじゃ終われないと思い調べたところこんなサイトさんがありました。
拡張for文を使う理由 - [Seasar]soichirooooo5の日記
リンク先を見てもらえればこの記事の書きたかった答えが書いてありますが、
せっかくなので最後までやります。
検証をArrayListでしかやっていませんので、配列とLinkedListでも検証してみます。
検証コード(配列)
public class Main {
public static void main(String[] args) {
int[] array = new int[10000000];
for (int i = 0; i < 10000000; i++) {
array[i] = i;
}
//開始
long start = System.currentTimeMillis();
//1000万ループ
for (int i = 0; i < array.length; i++) {
int tmp = array[i];
}
//終了
long end = System.currentTimeMillis();
System.out.println("for文: " + (end - start) + " ms");
//開始
start = System.currentTimeMillis();
//1000万ループ
for (int i : array) {
int tmp = i;
}
//終了
end = System.currentTimeMillis();
System.out.println("拡張for文: " + (end - start) + " ms");
}
}
結果
for文高速すぎるwww
拡張for文でオートボクシングが行われていたため、
正確に計測できていませんでした。
結果は変わらず。
※@swordone さんご指摘ありがとうございます。
for文: 3 ms
拡張for文: 3 ms
検証(LinkedList)
package qiita;
import java.util.LinkedList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> list = new LinkedList<>();
for (int i = 0; i < 10000000; i++) {
list.add(i);
}
//開始
long start = System.currentTimeMillis();
//1000万ループ
for (int i = 0; i < list.size(); i++) {
int tmp = list.get(i);
}
//終了
long end = System.currentTimeMillis();
System.out.println("for文: " + (end - start) + " ms");
//開始
start = System.currentTimeMillis();
//1000万ループ
for (Integer i : list) {
int tmp = i;
}
//終了
end = System.currentTimeMillis();
System.out.println("拡張for文: " + (end - start) + " ms");
}
}
検証結果
なんか5分くらい応答が返ってこないんですが。
※10分待っても終わらないので中止。
検証(LinkedList)②
終わらないので10万ループにして再検証
import java.util.LinkedList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> list = new LinkedList<>();
for (int i = 0; i < 100000; i++) {
list.add(i);
}
//開始
long start = System.currentTimeMillis();
//1000万ループ
for (int i = 0; i < list.size(); i++) {
int tmp = list.get(i);
}
//終了
long end = System.currentTimeMillis();
System.out.println("for文: " + (end - start) + " ms");
//開始
start = System.currentTimeMillis();
//1000万ループ
for (Integer i : list) {
int tmp = i;
}
//終了
end = System.currentTimeMillis();
System.out.println("拡張for文: " + (end - start) + " ms");
}
}
検証結果②
これはすごい!!
for文: 5695 ms
拡張for文: 36 ms
最後に
ArrayListや配列のように前から順番に操作するものは普通のfor文の方が早いことがわかった。
しかし、LinkedListでは圧倒的に差が出てしまった。
Listインターフェースをループさせるときに実体を気にするのはおかしいので、拡張for文でよいという結論に持って行けたと思う。
以下は参考サイトから引用です。
拡張for文を使っていれば、for文に渡されるオブジェクトがArrayListかLinkedListか分からない場合でも、「99%以上パフォーマンスが下がる」という心配をしなくて済みます。
また、拡張for文は、Listインタフェースの実装型をイテレータを使ったコードに変換し、配列の場合にはインデックスアクセスするコードに変換してくれます。
このコンパイラの翻訳によって、for文に渡すリスト又は配列に適したコードが生成されることが分かります。つまり、拡張forさえ使えば何も考えなくていい、何も心配しなくていいということです。まずいことがあればAPIの裏でなんとかしてくれる、ということです。
つまり何も考えずに拡張for文でよいということですね。
stream勉強しよう。