同期のためにVectorは使わない(VectorとCollections.synchronizedList)

よくJavaの解説には、VectorArrayListの違いはスレッドセーフであるかないかだということが書かれている。そのような解説を読むと、スレッドセーフな可変長配列がほしい場合にはVectorを使えば良いと思ってしまいそうだが、私は同期を目的としてもVectorは使うべきではないと思う。

Collections.synchronizedListメソッドによる同期

Vectorは単にArrayListのスレッドセーフ版ではなく、Java1.0のときに導入されたレガシークラスだ。Java1.2でCollections Frameworkが導入され、Vectorに代えてList(ArrayListやLinkedList)を用いることが推奨されるようになった。スレッドセーフなListを実現したいときには、VectorではなくCollections.synchronizedListメソッドを用いて次のように書くことができる

// スレッドセーフなArrayListを生成。
List list = Collections.synchronizedList(new ArrayList());

Vectorを使うべきでない理由

公式にVectorよりもCollections.synchronizedListメソッドを用いるべきだと書かれている資料は見つけることができなかった。Javaのリファレンスを見ても、VectorでもCollections.synchronizedListメソッドでも「同期を実現できる」としか書かれていない。


しかし、WEB上の意見を集めてみたところ、VectorではなくCollections.synchronizedListメソッドを用いるべきだ」という意見は複数見つかったが、逆は見つからなかったVectorを使うべきでない理由としては次のようなものがあった。

  • Vectorはレガシークラスなので、後方互換のために残されているだけで基本的に使うべきではない。
  • Collections.synchronizedListメソッドでListをラッピングすることで、明示的に同期化の意思を示すことができ、ソースの可読性が上がる。

もう一つ私が考える理由は次のようなものだ。

  • 内部的にリスト構造で実現されたスレッドセーフなListを作成することができる。

VectorArrayListのように内部的に配列で実装されているが、Collections.synchronizedListメソッドを用いれば、内部的にリスト構造で実装されているLinkedListのスレッドセーフ版も作ることができる。挿入や削除が繰り返し行われるようなスレッドセーフなListがほしい場合は、

// スレッドセーフなLinkedListを生成。
List list = Collections.synchronizedList(new LinkedList());

とした方がVectorを用いるよりパフォーマンスが上がるだろう。そして、これを使うのであれば、挿入、削除よりも参照を主とするスレッドセーフなListを作成するときには、Vectorを使うよりもCollections.synchronizedList(new ArrayList())を使った方が自然だろう

Vectorを使うべきでないと書かれていたページ

情報を収集時に見つけたページの一部を挙げておく。

Vectorについては、基本的には下位互換のために用意されているレガシ実装であることから、特に理由がなければ使わない方がよいでしょう。マルチスレッド環境下で利用しなければならない場合でも、同期化ラッパーを使うことをお勧めします。同期化の手順を明示的に踏むことで、同期化しなければならない、というプログラマの意図を、コード中で明らかにしておくことができるからです。

http://www.atmarkit.co.jp/fjava/javatips/136java026.html

Multithreading is not a valid reason to use Vector since you can do:

List myList = java.util.Collections.synchronizedList(new ArrayList());

Supporting legacy is really the only reason to choose Vector.

http://www.velocityreviews.com/forums/t132806-arraylist-vs-vector.html

A java.util.Vector should (speaking from a purist point of view) only be
used if you wish to support VMs prior to 1.2.
The same can be said for java.util.Hashtable.

If you need thread-safety you synchronize your java.util.List using
java.util.Collections.
This is the *nice* way of doing it.

http://www.velocityreviews.com/forums/t132806-arraylist-vs-vector.html

Vector/Hashtable are almost "legacy" classes. They were base classes right back in Java 1.0.2
Java 1.1 introduced the collections framework with the "Collection", "List", "Map" interfaces. The "old" classes Vector and Hashtable were retrofitted to fit in with the collections framework so that existing code wouldn't break.

However basically, ArrayList was a replacement for Vector, and HashMap was a replacement for Hashtable.

I use the newer classes in preference to the older ones.

(中略)

Also using the "synchronized***" methods lets you synchronize any implementation you want to.

http://forums.sun.com/thread.jspa?threadID=5273164&tstart=195

ArrayList は非同期ですが、そのために、
Collections.synchronizedList(new ArrayList());
というコードで、同期化されたリストを
取得可能な方法が提供されていますので、
Vector の存在は必須ではありません。

(中略)

コレクションフレームワークを設計した人にとっては、
Vector は邪魔だし、削除できるものならしたかった
だろうと思います。

http://okwave.jp/qa777956.html

Hashtableも使うべきでない(Collections.synchronizedMapメソッドを使う)

同様の理由で、もちろんHashtableも使うべきではない。Hashtableと同様の機能がほしければ、

// スレッドセーフなHashMapを生成
Map map = Collections.synchronizedMap(new HashMap());

と書けばいいし、ソート済みの特性がほしければTreeMapを用いて

// スレッドセーフなTreeMapを生成
Map map = Collections.synchronizedMap(new TreeMap());

と書くことだってできる。

(余談ですが、HashMapとTreeMapの速度比較について過去に検証を行いましたので、興味のある方はこちらをご覧下さい)

結論: VectorやHashtableは使わない。代わりにCollections.synchronized*メソッドを使う。

結局、VectorやHashtableはレガシークラスなので使わない、同様の機能がほしければ

List list = Collections.synchronizedList(new ArrayList());
Map map = Collections.synchronizedMap(new HashMap());

のように書く。また、Collections.synchronized*メソッドを用いれば、スレッドセーフなLinkedListやTreeMapだって作れる*1

*1:Collections.synchronizedSetメソッドを用いて、スレッドセーフなHashSetやTreeSetを作成することもできる。