memorandums

日々の生活で問題解決したこと、知ってよかったことなどを自分が思い出すために記録しています。

VectorとArrayListの違いを確認してみる

オブジェクト指向設計(2年次対象)の演習で以下を教科書として使用しています。

担当している講義や演習では教科書は使わず配布資料で済ますことが多いのですが、この科目だけは教科書を使っています。

理由はいくつかありますが、1年次の授業で以下のこの著者の本を使用していることが最も大きい理由です。本当にやさしい内容で、この世界の中では迷わず演習が進められるところが特徴と思います。ただ、この本を終えただけでは何か作れるようになるわけではないので、そこが評判のわかれるところと思います。

やさしいJava 第5版 (「やさしい」シリーズ)

やさしいJava 第5版 (「やさしい」シリーズ)

ちなみに今年の1年生からRubyに変更となり、本科目は選択科目のため今年が最後の開講になります。

この教科書には、Swingやコレクションクラスが前振りもなく登場します。

ですので、演習では補足しながら進めているわけですが、今回のエントリーがその補足説明の1つです。来年度はもうないのでわざわざ書くだけ無駄(自分のためにもならない)なのですが、Javaはまだまだ使われる言語ですのでいつか思い出したくなるときがあるかも。。。という期待を込めて書きます。

サンプルプログラムにコレクションクラスとしてArrayListVectorが出てきます。ただ、不思議なことにどちらだけ使うということではなくコロコロと変わります。さらにその選択基準も本文中には示されていません(と思います、説明されていたら。。。ごめんなさい)。

で、説明では「どちらも動的配列を実現するもので要素数の最大値を最初に定義することなく必要なときに必要なだけ追加したり参照できる」といったことを説明し「スレッドプログラミングをするときにはVectorを、そうでなければArrayListを」といった教科書的なことを説明します。

本当にそうなのか説明するには、VectorArrayListソースコードを見てsynchronizedを参照する方法もあるかもしれませんが、せっかく演習室での授業なので動かしてみて欲しいと思うわけです。

で、手頃なプログラムがないかな。。。できればわかりやすいやつ。。。と思ったのですが、あまりなく。全部手打ちすることを考えると短い方がよく。で、それをちょっと作ってみましたよ、ということです。

import java.util.Vector;
import java.util.ArrayList;

public class Main extends Thread {
	private Vector v;
	private ArrayList a;
	public static final int NUM = 10000; //調整項:マシンスペックにより増減させる
	public Main(Vector v, ArrayList a) {
		this.v = v;
		this.a = a;
	}
	public void run() {
		try {
			Thread.sleep((long)(Math.random() * 10)); //0-9ms待つ(スレッド間でremove()コールを衝突させるため)
		} catch (InterruptedException e) {} 
		v.remove(0); //先頭要素を1つ取り除く
		a.remove(0); //先頭要素を1つ取り除く
	}
	public static void main(String[] args) {
		Vector v = new Vector();
		ArrayList a = new ArrayList();
		for (int i = 0; i < Main.NUM; i++) { //コレクションに要素を詰め込む
			v.add(i);
			a.add(i);
		}
		Thread[] t = new Thread [Main.NUM];
		for (int i = 0; i < Main.NUM; i++) {
			t[i] = new Main(v, a);
			t[i].start(); //スレッド実行開始
		}
		for (int i = 0; i < Main.NUM; i++) { //全スレッドの終了を待つ
			try {
				t[i].join();
			} catch (InterruptedException e) {}
		}
		System.out.println("Vector = " + v.size()); //残要素数
		System.out.println("ArrayList = " + a.size()); //残要素数
	}
}

このプログラムのイメージは以下です。

(コレクションの中に)10000個の金貨の山があって、それを10000人の小人たち(スレッド)がワラワラ集まってきた1つずつ持って帰るイメージです。

f:id:ke_takahashi:20161011185531p:plain

Vectorでは、金貨の山から金貨を同時に取り出すのは1人だけ。1人が取っている間は他の人は行儀よく待ちます。なので最後には金貨の山はゼロになります。

一方、ArrayListの場合は、他の小人が取っていようがいまいがお構いなしです。2人以上の小人が同じ1つの金貨を持ち帰ることもあります。行儀は悪いけど欲はないですね。。。結果的に2人以上の小人が1つの金貨を持ち帰った分だけ、金貨の山には金貨が残ります。

説明的に間違ってはいないとは思いますが、2人以上の小人が1つの金貨を持ち帰るイメージはプログラム的には正しいような正しくないような。。。微妙な感じではありますが、とりあえず残数に違いがあり、(コレクション内にある)金貨を確実に1小人1つずつ持ち帰らせたいならVectorを使うべきだよ、というイメージが伝えられればいいかな。。。というところです。

ちなみにArrayListでもCollections.synchronizedListを使うとマルチスレッドに対応できるようですが。。。とりあえずこんなところです。