[Java]コレクションクラスと総称型のメモ

リスト、ハッシュマップ、キューなどのコレクションクラスについて。

コレクション・フレームワーク

Javaには充実したコレクションのフレームワークが存在する。

java collections frameworkなんかで画像を検索するとわかりやすい構成図が見つかるのでそれでも眺めましょう。

データ構造のメモ

  • リスト構造 : 前後のポインタによるつながりの構造
  • ハッシュ構造 : ハッシュをキーとしてオブジェクトを保持する構造
  • スタック構造 : 最後に入れ(push)たものは最初に出す(pop)構造、LIFO (Last In, First Out)
  • キュー構造 : 最初に入れ(offer)たものは最初に出す(poll)な構造、FIFO (First In, First Out)。イベントキューとか順番待ち構造のイメージ。

Map系 と Collection系 に大きく2分される

Map はコレクションするオブジェクトにハッシュキーを結びつけて保持するコレクションインターフェースである。

Collection はハッシュで管理しないコレクションインターフェースであり、Map と違い Iterable (参考: 拡張 for 文の真の実力を知り、反復処理を使いこなせ (1/3) - @IT) をスーパーインターフェースに持つ。

MapIterable を実装してないけど entrySet()Set として中身を取り出せるのでイテレーションには困らない。

配列みたいに一定の領域を確保してそこにポインタを保持するようなコレクションクラスは RandomAccess マーカーインターフェースを実装している。ArrayList, Vector は配列として保持しているそうだ。ArrayListVector 以外に RandomAccess を実装しているコレクションクラスは存在しない。

StackクラスもVectorクラスのサブクラスなのでランダムアクセス可能

高速性のみを求めた順番バラバラ配列は API 見た限りだとなさそう

テキトーに継承関係を並べてみる

沢山ある。とりあえず並べてみる。

Map

  • LinkedHashMap <= HashMap <= (abstract)AbstractMap + Map, Clonable, Serializable
    HashMap は順序は保持しない。LinkedHashMap は順序を保持する。
  • Hashtable <= (abstract)Dictionary + Map, Clonable, Serializable
    Hashtable は同期化マップだけど Collections.syncronizedMap(hashMap) を使うべき
  • TreeMap <= (abstract)AbstractMap + NavigableMap, Clonable, Serializable
    SortedMap が見当たらないのは NavigableMap のスーパーインターフェースに存在するから。(関連リンク:Java技術最前線 - 「Java SE 6完全攻略」第46回 ナビゲーション可能なマップ:ITpro)

メソッドは put, get, containsKey, containsValue, keySet, entrySet, remove, clear, size などを覚えておけばまず困らない。

Collection

Collection 系は大きく List, Set, Queue に分類される。 以下に書きだしたもの以外にもたくさんある…

List

  • (abstract)AbstractSequentialList <= (abstract)AbstractList <= (abstract)AbstractCollection <- Collection
  • ArrayList <= (abstract)AbstractList + List, RandomAccess, Clonable, Serializable
  • Vectorの継承実装はArrayListと同じ。Vectorは同期化リストだけど Collections.synchronizedList(list) を使うべき
  • LinkedList <= (abstract)AbstractSequentialList + Deque, Clonable, Serializable ランダムアクセスは遅いけど、挿入・削除が高速な双方向連結リスト。List系に含めたけどQueueの仲間でもある。

Set

  • LinkedHashSet <= HashSet <= (abstract)AbstractSet <= (abstract)AbstractCollection + Set
    HashSet は順番が保証されない集合。LinkedHashSetは連結リスト同様に順番が保証されている集合。
  • TreeSet <= (abstract)AbstractSet + NavigableSet, Clonable, Serializable
    何らかの順序で整列している集合
  • CopyOnWriteArraySet <= (abstract)AbstractSet

Queue

  • PriorityQueue
  • PriorityBlockingQueue
  • ArrayBlockingQueue
  • SynchronousQueue
  • LinkedBlockingQueue
  • LinkedTransferQueue
  • ConcurrentLinkedQueue
  • DelayQueue

色々ありすぎてよくわからない…

弱参照なコレクションクラス

  • WeakRefference
  • WeakHashMap

ArraysCollections

配列のためのユーティリティクラス。同期化したりとかいろいろできる。 こいつらは何かを継承したり実装したりはしていない。

総称型のメモ

普通の(業務)Java アプリケーションでは配列をなるべく使用しない方がよい - 達人プログラマーを目指して

Java の配列 [] は共変で型安全ではない(ArrayStoreExceptionが飛ぶことがある)。総称型の配列は警告なしに new できない。

ワイルドカード型のメモ

Java 総称型のワイルドカードを上手に使いこなすための勘所 - 達人プログラマーを目指してを読んだメモ。

List<?>List<Object> は違う

ワイルドカード型 は "全ての型" ではなく "何か分からないが特定の1つの型"。 従って List<?> 型のコレクションに Object クラスを追加することはできない。 たとえば ?String だとしたら無理だよね。

非変性

ワイルドカード型を使わない総称型の変数は非変性。

List<Object> list = new ArrayList<String>(); // コンパイルエラー

? extends 上限型

共変性(covariant)の総称型を指定できる。出力のみ型安全が保証される総称型として使用出来る。ポリモーフィズムと似てるから、そんなに分かりにくくないかなと。

List<? extends Number> list1 = Arrays.asList(1.23);
List<? extends Number> list2 = Arrays.asList(456);
List<? extends Number> list3 = Arrays.asList(new BigDecimal(789));

// ? は Number型 あるいは Numberのサブクラス なので
// 変数から Number型の値を取り出せる
Number val1 = list1.get(0);
Number val2 = list2.get(0);
Number val3 = list3.get(0);

// ? の型は不明なので 値のセットは不可能(nullのみOK)
// たとえば ? は Number や Integer, Double, Object などの可能性があるが
// ? = Number とした場合は、 Integer や Double, Object はセットできないし、
// ? = Integer とした場合は、 Double, Object がセットできない。
// 従って ? の型は不明なので値のセットは不可能(nullのみOK)
list1.add(null);
list1.add(val1); // コンパイルエラー
list2.add(val2); // コンパイルエラー
list3.add(val3); // コンパイルエラー

? super 下限型

反変性(contravariant)の総称型を指定できる。入力時に下限型の型安全が保証される総称型として使用出来る。 たとえば ? super IllegalArgumentException の場合、 ?IllegalArgumentException, RuntimeException, Exception, Object のいずれかとなる。 従って、安全に取り出せるのは Object 型のみとなる。

List<? super IllegalArgumentException> list1 = Arrays.asList(new IllegalArgumentException());
List<? super IllegalArgumentException> list2 = Arrays.asList(new RuntimeException());
List<? super IllegalArgumentException> list3 = Arrays.asList(new Exception());

Object val1 = list1.get(0);
Object val2 = list2.get(0);
Object val3 = list3.get(0);

逆に入力は IllegalArgumentException 型およびそのサブクラスならば型安全に可能となる。

List<? super IllegalArgumentException> list = new ArrayList<Object>();

list.add(new Object()); // コンパイルエラー
list.add(new Exception()); // コンパイルエラー
list.add(new RuntimeException()); // コンパイルエラー
list.add(new IllegalArgumentException());
list.add(new NumberFormatException());
list.add(new IllegalThreadStateException());

IllegalArgumentException の継承ツリーは以下の通り。

Object <- Exception <- RuntimeException <- IllegalArgumentException

また IllegalArgumentException のサブクラスには NumberFormatExceptionIllegalThreadStateException が存在する。

IllegalArgumentException <- NumberFormatException

IllegalArgumentException <- IllegalThreadStateException

上限も下限もないワイルドカード型

  • 全ての総称型のスーパータイプ
  • あんまり使われない。レガシーなライブラリに対して使うことがある。
  • ワイルドカードキャプチャというテクニックを使用するときに使う
Share
関連記事