配列 vs ArrayList、速くて軽いのはどっち!?

Javaのソースで、たまに「ArrayListを使っているけど、代入する要素数は決まっている」というのを見かけます。
こういうのを見ると、「要素数が決まっているなら、可変のもの(ArrayList)じゃなくて固定のもの(配列)を使った方が速くてメモリ使用量も少なくて済むんじゃないのかな」といつも思います。


でも、よく考えたら試していない・・・。


そこで実験。
「配列に代入」するのと「ArrayListに追加」するのは、どちらが速い・軽い・うまい!?

                                                                                  • -

ソースは最後に添付します。
先に結果だけ。

配列とArrayListでは、
 初期容量を指定しなければ、配列の方が3倍速い
 初期容量を指定しても、配列の方が2倍速い。
 (ただ、10,000,000万回ループしてちょっとの差が出るぐらい。どちらでも十分に速い)

配列とArrayListでは、
 初期容量を指定しなければ、ArrayListの方がメモリ使用量が1割多い(ゴミができる)。
 初期容量を指定していれば、メモリ使用量はまったく変わらない。


この結果から、「配列の方がいいけど、初期容量を指定すればArrayListでも問題なし」ということが分かりました。
というより、初期容量を指定しているかどうかの方が重要


なので結論としては、

  • 素数が固定ならば、配列の方がほんの少しだけいい。
  • ArrayListを使うなら、要素数を予測して使いましょう。
    • List list = new ArrayList(必ず指定する!);


ちなみに、テストしたマシンはスペックが高め*1なので、差がつきにくくなっているかもしれません。
なので、具体的な数値が気になる方は、お手元のPCで実験してみてください。


使用したソース:

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class Main {
	private static final int SIZE = 10000000;
	
	public static void main(String[] args) {
		long start, end;
		System.out.println("ListSize : " + SIZE);
		
		Random random = new Random();
		String[] sample = new String[SIZE];
		for(int i = 0; i < sample.length; i++){
			sample[i] = Integer.toString(random.nextInt());
		}

		for(int i = 0; i < 5; i++){
			start = System.currentTimeMillis();
			arrayTest(sample);
			end = System.currentTimeMillis();
			output("配列", start, end);

			System.gc();
			
			start = System.currentTimeMillis();
			arrayListTest(sample);
			end = System.currentTimeMillis();
			output("リスト", start, end);
			
			System.gc();
		}
	}
	
	private static void arrayTest(String[] sample){
		String[] array = new String[SIZE];
		for(int i = 0; i < sample.length; i++){
			array[i] = sample[i];
		}
	}
	
	private static void arrayListTest(String[] sample){
		List<String> arrayList = new ArrayList<String>(SIZE);	// ここでArrayListの初期容量を指定
		for(int i = 0; i < sample.length; i++){
			arrayList.add(sample[i]);
		}
	}
	
	private static void output(String message, long start, long end){
		long time = end - start;
		long usedMemory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
		
		System.out.print(message + " -> ");
		System.out.println("時間:" + time + ", " + "メモリ使用量:" + usedMemory);
	}
}

*1:Intel Core i7 920, メモリ 3GBトリプルチャンネル