ArayListリストの異なるエルゴード方式の性能比較


今私たちはList<Integer>の例の平均値を要求すると仮定して、次の2つの方法で計算できます。
        1. foreach方式でリストを巡回して平均値を計算します。
public static int average(List<Integer> list) {
    int sum = 0;
    //     
    for (int i : list) {
        sum += i;
    }
    return sum / list.size();
}
        2. 下付きでリストにアクセスして平均値を計算します。
public static int average(List<Integer> list) {
    int sum = 0;
    //     
    for (int i = 0, size = list.size(); i < size; i++) {
        sum += i;
    }
    return sum / list.size();
}
        上の二つの実現方法はただ書き方が違っているだけと皆が思っているかもしれませんが、実行過程は同じです。小さな数のデータを処理するには、効率的にはそれほど違っていませんが、大きな数のデータを処理するには、後者のほうが前者よりずっと効率的です。例えば100万本のデータを処理すると、前者の実行にはおよそ42 msが必要です。後者は25 msしか必要ではありません。なぜ下付きの方式で配列を巡るとこんなに高い性能が上がるのですか?
        Javaのforeach文法はiteratorの変形用法であることを知っています。つまり上のforeachは下のコードと等価です。
for (Iterator<Integer> i = list.iterator(); i.hasNext();) {
    sum += ((Integer)iterator.next()).intValue();
}
        lsit.iterator()が戻ってきたディズエターのオブジェクトは何ですか?JDKのソースコードを見ると、AbstrayListクラスはAbstractListクラスに継承され、AbstraacListはiterator方法に対して次のように実現されています。
public Iterator<E> iterator() {
    return new Itr();
}
        方法は直接Itrオブジェクトを作成して返します。ItrクラスはAbstractListクラスの内部クラスです。ItrクラスはIteratorインターフェースを実現しました。Itrクラスの部分ソースコードは以下の通りです。
private class Itr implements Iterator<E> {
    /**
     * Index of element to be returned by subsequent call to next.
     */
    int cursor = 0;

    /**
     * Index of element returned by most recent call to next or
     * previous.  Reset to -1 if this element is deleted by a call
     * to remove.
     */
    int lastRet = -1;

    public boolean hasNext() {
        return cursor != size();
    }

    public E next() {
        checkForComodification();
        try {
            E next = get(cursor);
            lastRet = cursor++;
            return next;
        } catch (IndexOutOfBoundsException e) {
            checkForComodification();
            throw new NoSuchElementException();
        }
    }
}
        上のコードからも、配列全体を巡回するために指し示し配列の下付きスケーリングをインクリメントすることによって、ディケンサも同じですが、下付きでリストにアクセスして平均値を計算する方法とは違います。関数は、配列の下付きが配列の最後に移動したかどうかを判断しますが、下付きアクセス方式ではこれをチェックする必要はありません。このほかに、ディエゼルの next()メソッドには、下記のアクセス方式にないcheckForCompodification()メソッドとlastRet変数の割当値操作もあります。これはつまり、foreach方式で時間を経る原因です。
        ここでは、ディケンサを使うデメリットを言っているわけではありません。ここで私は皆さんにディエゼルについて深く理解してもらい、自分の勉強の成果として残しておきます。少量のデータを扱う場合や性能に過大な要求がない場合は、ローズマリーを使うほうが便利で簡潔です。
参考文献:「高品質コードの作成:Javaプログラムを改善するための151の提案」67。