Collectionの全ての要素にアクセスする為には,toArray()かiterator()
のどちらかを使う必要があります.それではその使い分けを考えてみます.
無駄に長い*1ので,まとめだけ読むと実用的です^^;

テストプログラム

コンパイルにはJ2SE5.0以上が必要です*2

import java.util.*;

class Array_Iterator
{
    static class Student implements Comparable<Student>
    {
        Student(String name, int score){Name=name; Score=score;}
        public String Name;
        public int Score;
        
        public String toString(){return Name+":"+Score;}
        public int compareTo(Student s){return Score-s.Score;}
    }
    
    public static void main(String[] argv)
    {
        System.out.println("By_toArray()");
        By_toArray();
        System.out.println("By_iterator()");
        By_iterator();
    }
    
    public static void By_iterator()
    {
        ArrayList<Student> StudentList=new ArrayList<Student>();
        
        StudentList.add(new Student("太郎", 60));
        StudentList.add(new Student("次郎", 40));
        StudentList.add(new Student("三郎", 80));
        StudentList.add(new Student("花子", 70));
        Collections.sort(StudentList);
        
        //イテレータを使って全員ボーナスを追加し毎回ソートしなおす?
        it=StudentList.iterator();
        Student Now;
        while(it.hasNext())
        {
            Now=it.next();
            Now.Score+=(int)(Math.random()*100);
            Collections.sort(StudentList);
            System.out.println(Now.Name+"にボーナス追加 ");
        }
        //表示
        it=StudentList.iterator();
        while(it.hasNext())
        {
            System.out.println(it.next());
        }
    }
    
    public static void By_toArray()
    {
        ArrayList<Student> StudentList=new ArrayList<Student>();
        
        StudentList.add(new Student("太郎", 60));
        StudentList.add(new Student("次郎", 40));
        StudentList.add(new Student("三郎", 80));
        StudentList.add(new Student("花子", 70));
        Collections.sort(StudentList);
        
        //表示
        System.out.println("元のスコア");
        Student[] aStudent=StudentList.toArray(new Student[StudentList.size()]);
        int Length=aStudent.length;
        for(int i=0; i<Length; i++)
        {
            System.out.println(aStudent[i]);
        }
        
        //配列を使って全員にボーナスを追加し毎回ソートしなおす
        aStudent=StudentList.toArray(new Student[StudentList.size()]);
        Length=aStudent.length;
        for(int i=0; i<Length; i++)
        {
            aStudent[i].Score+=(int)(Math.random()*100);
            Collections.sort(StudentList);
            System.out.println(aStudent[i].Name+"にボーナス追加 ");
        }
        //表示
        aStudent=StudentList.toArray(new Student[StudentList.size()]);
        Length=aStudent.length;
        for(int i=0; i<Length; i++)
        {
            System.out.println(aStudent[i]);
        }
    }
}

サンプルとしては無駄に長いですが,やっている事は単純です.

  • 学生の情報をStudentListに入れた後にソート
  • 全て学生にボーナスを与えるたびにソート をイテレータと配列のそれぞれを使って実装しています.

実行結果

By_toArray()
元のスコア
次郎:40
太郎:60
花子:70
三郎:80
次郎にボーナス追加
太郎にボーナス追加
花子にボーナス追加
三郎にボーナス追加
次郎:123
三郎:135
太郎:139
花子:144
By_iterator()
次郎にボーナス追加
花子にボーナス追加
次郎にボーナス追加
次郎にボーナス追加
太郎:60
三郎:80
花子:161
次郎:277

By_iteratorがおかしいぞ

By_iterator()の部分を見てみましょう.全員にボーナスを追加しているはずの場所で

次郎にボーナス追加
花子にボーナス追加
次郎にボーナス追加
次郎にボーナス追加

と,次郎に3回ボーナスを追加していますね.これは何故でしょうか.
これは,最初に次郎にボーナスを追加した後のソートで,次郎のリスト内での位置が
1番目から3番目に移動しまった為です.
イテレータを使うと,next()を読んだ時点で,指定した場所にある要素にアクセスする事になるので,
このような事が起こってしまいます.つまり

次郎:40-太郎:60-花子:70-三郎:80

の順で並んでいたリストの先頭,次郎にボーナスを加えると

太郎:60-花子:70-次郎:75-三郎:80

となります.2回目のボーナスの追加では,2番目の要素の学生にボーナスを与えますが,
この時点での2番目の要素は,元々次郎の次にいた太郎ではなく花子です.
よって,花子にボーナスがあたえられます.

太郎:60-花子:73-次郎:75-三郎:80

次も同様に進んでいき,結果的に次郎が三回ボーナスを受ける事になります.
これは,プログラマが望んだ動作ではありません.

By_toArrayでは

対して,toArray()を使った場合では,toArrayを読んだ時点での要素の順で配列に要素が代入されます.
よって,その後の要素の順の変更がaStudent(学生の配列)に影響を与えません.
なので,n番目の学生にボーナスを与えるという処理が問題なく動作するのです.

まとめ

iterator()を使って要素にアクセスする場合,途中で元のCollectionの要素の順を変更してはいけない*3という事がわかりました.
また,シングルスレッドの場合注意すれば守れますが,マルチスレッド(含むGUI)の場合難しくなってきます.
使い所を間違えないように注意する必要がありそうです.
toArray()を使った場合は,アクセス中に要素の順を変えても配列に影響はありません.
toArray()を使う分には大きな問題はないように思います.

イテレータはiterator()を呼び出した後のコレクションの変化の影響を受けるが,
配列ではtoArray()を読んだ後のコレクションの変化が,配列には影響を与えない.

という事を覚えておけば大丈夫でしょう.

コメント


お名前:

*1 加えて文章が変
*2 1.4未満対応にすればよかった・・・
*3 もちろん,要素の削除も禁止です
添付ファイル: fileArray_Iterator.java 928件 [詳細]