S-JIS[2007-03-26/2015-06-20] 変更履歴

コレクションクラス

Javaのリストやマップ等のコレクション(要素の集合・グループ)クラス。


コレクション一覧

コレクション クラス 備考 旧クラス 更新日
リスト List リストのインターフェース。    
LinkedList いわゆる普通の双方向リスト。    
ArrayList インデックスを使って途中の要素にアクセスするのが高速なリスト。
ただし途中の要素の追加・削除は遅い。
MTセーフなArrayList
Vector  
SortedList 自動的にソートされるリスト、というものは無い。
(重複無しなら)TreeSetを使うか、Collections#sort()でソートする。
  2008-07-05
emptyList() 常に空のリスト。→空のコレクション EMPTY_LIST 2008-07-09
BitSet booleanの可変長配列のようなもの。
インデックスを使ったアクセスを行うが、Listは実装していない。
名前は「Set」だが、Setは実装していない。
  2010-02-13
マップ Map 連想配列(マップ)のインターフェース。    
HashMap hashCode()を利用したマップ。
HashMapの仕組み
HashMapのキーについての注意点
MTセーフなHashMap
Hashtable(ハッシュテーブル)  
SortedMap キーがソートされるマップ(インターフェース)。
具象クラスはTreeMap
  2008-07-05
NavigableMap (完全一致でなく、)近いキーを取得できるマップ(インターフェース)。
具象クラスはTreeMap
  2008-07-24
TreeMap キーがソートされるマップ(実体)。    
LinkedHashMap キーの順序がputした順番であることが保証されているマップ。
同じキーをputした場合、順序は最初にputした位置で、値は後からputしたものになる。
  2008-07-05
emptyMap() 常に空のマップ。→空のコレクション EMPTY_MAP 2008-07-09
EnumMap 列挙型をキーにとる場合に効率がよいマップ。JDK1.5以降。    
Properties プロパティー(プロパティーファイルの内容)    
WeakHashMap キーを弱参照で保持するハッシュマップ   2008-07-14
DoubleOrderedMap Apache commonsの双方向マップ。
つまり値を指定してキーを取得する事も出来る。
  2010-02-13
BiMap
HashBiMap
google-collectionsの逆方向マップを取得できるマップ。   2010-02-13
セット
(集合)
Set セット(マップに対し、キーだけを保持するような物)のインターフェース。    
HashSet HashMapのキーのセット。    
SortedSet ソートされているSetのインターフェース。   2008-07-05
TreeSet TreeMapのキーのセット。(ソートされている)    
emptySet() 常に空のセット。→空のコレクション EMPTY_SET 2008-07-09
EnumSet 列挙型の論理和を保持する為のセット。JDK1.5以降。    
BitSet 名前は「Set」だが、Setの具象クラスではない。   2010-02-13
スタック LinkedList LinkedListは双方向リストであってスタックではないのだが、synchronizedで同期化されていないスタッククラスは無い模様。
Stackクラスは 何故かVectorを継承していてsynchronizedで同期化されている為、重い)
そこで、LinkedListで代用する。
push→add、pop→removeLastでOK。
(ただしremove系は、値が存在しないと例外が発生する)
Stack 2007-06-12
ArrayDeque (JDK1.6以降)
配列を使って実装されているので、スタックの最大数が変わらないならLinkedListより高速。
(最大数が変わると拡張処理が発生する)
push→addLast、pop→pollLast または
push→addFirst、pop→pollFirst
  2009-01-04
キュー Queue キューのインターフェース(JDK1.5以降)
追加はadd又はoffer、取得はremove又はpoll(例外を返すかどうかが異なる)。
JDK1.4までは、Queue自体、コレクションクラスとしては用意されていなかった。 2007-12-07
LinkedList 追加→addLast(add)、取得→removeFirst(poll)   2008-07-07
LinkedBlockingQueue     2008-07-24
ArrayBlockingQueue     2008-07-24
双方向キュー Deque 双方向キューのインターフェース(JDK1.6以降)
「デック」と読むらしい。
  2008-07-24
ArrayDeque (キューに溜める最大数が固定なら)LinkedListより高速。
(配列内の開始位置と終了位置を保持してずらすので、ArrayListをキューに使うのと違って効率が良い)
  2009-01-04
LinkedList     2008-07-24
LinkedBlockingDeque     2008-07-24

Verctor・Hashtableは、それぞれArrayList・HashMapのMTセーフバージョンという位置づけ。
すなわち、ほとんどの操作メソッドがsynchronizedで同期化されているので、動作が遅い。
マルチスレッドで複数のスレッドから同一のコレクションインスタンスにアクセスしない限り MTセーフ版を使う必要は無いので、普通はArrayListやHashMapを使うのがよい。
仮にMTセーフ版を使う必要があるとしても、JDK1.5で追加された、より高速なMTセーフなコレクションを使った方が良さげ。


総称型

JDK1.5から、リストマップ総称型を使うようにコードが書き換えられた。[2007-05-02]
JDK1.4までは、String型のみを扱いたいと思っても(Object型なので)何でも入れられたので、プログラマーが気をつける必要があった。
総称型が導入されたことで、違う型で扱おうとするとコンパイルエラーになるので間違いが発見しやすくなった。
(Object型から目的の型へのキャストも減って、コーディングもすっきりするようになった)

	List<String> list = new ArrayList<String>();	//String限定のリスト
	list.add("abc");
	String s = list.get(0);
	Map<String,Integer> map = new HashMap<String,Integer>();	//キーがString、値がIntegerのマップ
	map.put("abc", new Integer(123));
	Integer i = map.get("abc");

プリミティブ型を指定することは出来ないが、JDK1.5からは自動ボクシングという機能も導入されたので、ラッパークラスを指定すれば( ソース上の見た目としては)扱える。

	Map<String,Integer> map = new HashMap<String,Integer>();	//キーがString、値がIntegerのマップ
	map.put("abc", 123);
	int i = map.get("abc");

Iteratorもコレクションクラスの型を引き継いでくれる。[2007-06-11]

//リスト
	for (Iterator<String> i = list.iterator(); i.hasNext();) {
		String val = i.next();
		System.out.println(val);
	}
	for (String val : list) {
		System.out.println(val);
	}
//マップ
	for (Iterator<String> i = map.keySet().iterator(); i.hasNext();) {
		String key = i.next();
		System.out.println(map.get(key));
	}
	for (String key : map.keySet()) {
		System.out.println(map.get(key));
	}

また、限定する型を指定しない(JDK1.4までのような)やり方も今までどおり使う事が出来る。
(コンパイルすると警告(unchecked)が出るけど)


空のコレクション

java.util.Collectionsクラスに、空のコレクション(リストマップセット)のインスタンスが用意されている。[2008-07-09]
中身が空のコレクションは、これらを使うことが出来る。

例えばリスト(EMPTY_LIST)の場合、以下のようにして使用する。

	List list = Collections.EMPTY_LIST;
	System.out.println(list.size());	//常に0
//×	list.add("abc");			//常にUnsupportedOperationExceptionが発生する
//×	Object obj = list.get(0);		//常にIndexOutOfBoundsExceptionが発生する

マップ(EMPTY_MAP)なら、get()は常にnullを返す、という具合。

つまり、値取得系のメソッドは値が無い状態の動作をし、値をセットする系統のメソッドは例外が発生するので 常に“空の状態”が保たれる、というわけ。


ところで、JDK1.5でコレクションクラスは総称型を使用するようになった為、EMPTY_LISTやEMPTY_MAPを直接使うと、キャストに関して警告が出てしまう。

	List<String> list = (List<String>)Collections.EMPTY_LIST; //「実際には消去された型Listに対してチェックを行います」という警告

そこで、指定した型の空コレクションを返すメソッド(emptyList()emptyMap()emptySet())が用意された。

例えばリスト(emptyList())の場合、以下のようにして使用する。

	List<String>  slist = Collections.emptyList();
	List<Integer> ilist = Collections.emptyList();

実際に返って来ているのはEMPTY_LISTそのまま。
emptyList()の定義にはメソッドの総称型が使用されているので、変数宣言に書いた<型>に応じて自動的に“キャストされたEMPTY_LIST”になってくれる。


空のコレクション(リスト等)そのものは、上記のようにCollections.emptyList()の代入先に合わせて型引数が指定される。[2009-02-09]
しかしそのIteratorを直接取ろうとすると、そのままでは出来ない。

	Iterator<String> i = Collections.emptyList().iterator();	// キャストできないというコンパイルエラー
	Iterator<?>      j = Collections.emptyList().iterator();	// OK
 	Iterator<Object> k = Collections.emptyList().iterator();	// OK

ワイルドカード<?>や<Object>は大丈夫だが、それではあまり嬉しくない…。
こういう場合は、明示的な型指定を使うと良い。

	Iterator<String> i = Collections.<String>emptyList().iterator();	// OK
	public Iterator<String> getIterator() {
		if (this.list == null) {
			return Collections.<String>emptyList().iterator();
		} else {
			return this.list.iterator();
		}
	}

1つだけ値を持つ不変コレクション

空のコレクションと似た感じで、値を1つだけ持つ(変更不能な)コレクションを作ることが出来る。[2008-07-09]

コレクション メソッド 備考
リスト singletonList(T obj)  
マップ singletonMap(K key, V value)  
セット singleton(T obj) なんでsingletonSet()って名前じゃないんだろう?
	List<String> list = Collections.singletonList("abc");
	System.out.println(list.size());	//常に1
	for (String s : list) {
		System.out.println(s);
	}

このsingletonシリーズも 値の変更が不可能。
つまり「list.set(0,"zzz")」等の呼び出しを行うとUnsupportedOperationExceptionが発生する。

…しかし、値が1個しか入ってないコレクション(集合)って、あまり使い道が思い浮かばないなぁ…。 でもまぁ、デフォルトで唯一の値を持っているリストとかマップとかを作ることはあるのかも。


変更不可能なコレクション

1つだけ値を持つコレクションの汎用化版と言うか、内容を変更できないコレクションを作ることが出来る。[2008-07-09]

コレクション メソッド
リスト unmodifiableList(List)
マップ unmodifiableMap(Map)
unmodifiableSortedMap(SortedMap)
セット unmodifiableSet(Set)
unmodifiableSortedSet(SortedSet)
一般 unmodifiableCollection(Collection)

引数の種類(型)が全部違うんだから、メソッド名を同じにしてオーバーロードを作っても良さそうな気がするんだけど、そうしない理由があるのかな?

普通にリストを作ってunmodifiableList()を通すことにより、不変のリストが作られる。

	private static final List<String> CONST_LIST;
	static {
		List<String> list = new ArrayList<String>(2);
		list.add("abc");
		list.add("def");
		CONST_LIST = Collections.unmodifiableList(list);
	}

	static void sample() {
		System.out.println(CONST_LIST.size());

		for (int i = 0; i < CONST_LIST.size(); i++) {
			System.out.println(CONST_LIST.get(i));
		}

//×		CONST_LIST.add("zzz");	//常にUnsupportedOperationExceptionが発生する
	}

この不変リストの実体はラッパークラスであり、値を取得する系統のメソッドは元のリストの同メソッドを呼び出し、内容を変更する系統のメソッドは例外が発生するようになっている。

また、元のリストがRandomAccessインターフェースを実装していれば、不変リスト(ラッパークラス)もRandomAccessをimplementsしたクラスが使われる。
つまり、「if (list instanceof RandomAccess)」による判断はそのまま出来るということだ。ちょっと親切かも。あまり使うことは無さそうな気はするが(爆)


この不変用ラッパークラスは、元のリストを保持している。
したがって、元のリストの内容を変更すれば、不変リストの方も変更された値が取得できる。

このunmodifiableシリーズはけっこう便利そうだ。
値の変更は自分のみが行い、他人に変更させたくない場合もクローンを作らずに返せるわけだから。

	private final List<String> privateList = new ArrayList<String>();
	private final List<String> publicList  = Collections.unmodifiableList(privateList);

	/**
	 * リスト取得.
	 * @return リスト(必ずnull以外。このリストは変更できない)
	 */
	public List<String> getList() {
		return publicList;
	}

	private void addList(String str) {
		privateList.add(str);
	}

Java目次へ戻る / 新機能へ戻る / 技術メモへ戻る
メールの送信先:ひしだま