はじめに
C++ だと可変長配列で std::vector
を使いますが、Java では普通 java.util.ArrayList
を使い、同じような機能を持つ(らしい) java.util.Vector
は使われません。
- 同期をとるため遅いらしい
- 同期リストが欲しい場合は
java.util.Collections.synchronizedList()
を使った方がいいらしい
みたいな話を昔聞いてそうなのかーと思いこれまで気にしてきませんでしたが、ふとに気になったので OpenJDK のソースを見てみました。
Vector のメソッド
確かに各メソッドに synchronized 句がついています。同期が必要ない場面では間違いなく ArrayList
を使うべきそうです。
...
public synchronized E get(int index) {
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
return elementData(index);
}
...
public synchronized boolean add(E e) {
modCount++;
add(e, elementData, elementCount);
return true;
}
...
Collections.SynchronizedList との違い
では、Collections.SynchronizedList(new ArrayList<>())
とし ArrayList
をラップしたものと Vector はどう違うのか?
パッと見て違うのは、Iterator の挙動です。Vector
では Iterator のメソッドでもほかのメソッド同様、ロックを取得しています。
private class Itr implements Iterator<E> {
...
public E next() {
synchronized (Vector.this) {
checkForComodification();
int i = cursor;
if (i >= elementCount)
throw new NoSuchElementException();
cursor = i + 1;
return elementData(lastRet = i);
}
}
...
SyncronizedList が継承している SynchronizedCollection の場合、Iterator() はラップしている型のものをそのまま返しています。
static class SynchronizedCollection<E> implements Collection<E>, Serializable {
private static final long serialVersionUID = 3053995032091335093L;
final Collection<E> c; // Backing Collection
final Object mutex; // Object on which to synchronize
...
public Iterator<E> iterator() {
return c.iterator(); // Must be manually synched by user!
}
リストへの繰り返し処理を行う場合には、単一のメソッド内でのみでのロック取得では不十分で、結局繰り返し処理全体でロックする必要があり、それは自分でやってね("Must be manually synched by user!") ということなのでしょうたぶん。
まとめ
-
Vector
は各メソッドに synchronized がついている - SynchronizedList とは iterator の挙動が異なる