目次
Java には 動的配列 ArrayList
と Vector
があります。 (それに加えて LinkedList
というのもあります。) いずれも Collection
インターフェース、 List
インターフェース を実装しており、 使い方はほとんど同じです。 歴史的には Vector
のほうが ArrayList
よりも先に作られました。
この記事では、 それら2つのクラスの違いと、それを検証するサンプルコードを紹介します。
環境
このページのコードは、 下記の環境でコンパイル・実行しています。
- OpenJDK 11
ArrayList と Vector の違い
ArrayList
はマルチスレッドで複数のスレッドからアクセスされると、正しく動作しないことがあります。 一方で Vector
はスレッドセーフです。
ArrayList
に synchronizedList
メソッド を施すと、 スレッドセーフになります。
検証コード
ArrayList
と Vector
の違いをコードで確かめてみます。
ArrayList
, Vector
に、 1,024 の要素を追加してすべて取り除くというプログラムで、 追加された要素が正しく取り除かれるのか検証します。 ArrayList
については synchronizedList
を施したものとそうでないものと、両方検証します。
比較対象
Vector
ArrayList
synchronizedList
を施したArrayList
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 |
package com.improve_future; import java.util.*; public class Main { /** * Size of the Vector and the ArrayList for test */ private static final int SIZE = 1024; class ThreadRunner extends Thread { private List list; ThreadRunner(List list) { this.list = list; } @Override public void run() { try { Thread.sleep((long) (Math.random() * 10)); } catch (InterruptedException ignored) { } list.remove(0); } } public static void main(String[] args) throws InterruptedException { new Main().test(new Vector(), "Vector"); new Main().test(new ArrayList(), "ArrayList"); // if the ArrayList is changed to the synchronized one, // it is handled as vector and all the elements are removed at last. new Main().test( Collections.synchronizedList(new ArrayList()), "SynchronizedArrayList"); } private void test(List list, String title) throws InterruptedException { // Add elements. for (int i = 0; i < SIZE; ++i) list.add(i); // Remove elements by threads. Thread[] threadArray = new Thread[SIZE]; for (int i = 0; i < SIZE; ++i) (threadArray[i] = new ThreadRunner(list)).start(); for (Thread thread : threadArray) thread.join(); // Show remain. System.out.println(title + ": " + list.size()); } } |
ArrayList
, Vector
を List
インターフェース を通して扱います。 test
メソッド を実行すると、検証できるようにしています。 具体的には次のコードを順次実行します。
要素を追加するコード
for (int i = 0; i < SIZE; ++i) list.add(i);
で List
に要素を追加します。 この部分で 1,024 の要素を追加します。
まだマルチスレッドにしていませんから、 確実に 1.024 の要素を追加できます。
要素を取り除くスレッドを実行するコード
次の部分のコードでスレッドを生成・実行します。
1 2 3 |
Thread[] threadArray = new Thread[SIZE]; for (int i = 0; i < SIZE; ++i) (threadArray[i] = new ThreadRunner(list)).start(); |
各スレッドは最初のひとつの要素を取り除くようになっています。 そのスレッドを 1,024 個 起動して、 全ての要素を取り除きます。
スレッドの内容は次のようになっています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class ThreadRunner extends Thread { private List list; ThreadRunner(List list) { this.list = list; } @Override public void run() { try { Thread.sleep((long) (Math.random() * 10)); } catch (InterruptedException ignored) { } list.remove(0); } } |
途中で Thread.sleep
を実行しているのは、 このようにして要素を取り除く順番が乱れるようにしておかないと、スレッドを使っていないのと同じ結果になってしまうからです。
結果を出力するコード
System.out.println(title + ": " + list.size());
で最後に残された要素数を出力しています。
結果
結果は次のように出力されました。 ArrayList
をそのまま使った場合のみ、 全ての要素が取り除かれていませんね。
1 2 3 |
Vector: 0 ArrayList: 27 SynchronizedArrayList: 0 |