最近、J2EE の本でコレクション フレームワークについての非常に優れた説明を見つけたので、それをフィルタリングして投稿し、オブジェクト コレクションを管理するためのインターフェイスとクラスを提供します。以下は各コンポーネントの説明です。
コレクションインターフェース
コレクションは最も基本的なコレクション インターフェイスです。コレクションはオブジェクトのセット、つまりコレクションの要素を表します。コレクションによっては、同一の要素を許可するものと許可しないものがあります。ある種のものとそうでないものがあります。 Java SDK は Collection を直接継承するクラスを提供しません。Java SDK が提供するクラスはすべて、List や Set など、Collection を継承する「サブインターフェイス」です。
Collection インターフェイスを実装するすべてのクラスは、空の Collection を作成するためのパラメーターなしのコンストラクターと、新しい Collection を作成するための Collection パラメーターのコンストラクターという 2 つの標準コンストラクターを提供する必要があります。入力 Collection には同じ要素があります。後者のコンストラクターを使用すると、ユーザーはコレクションをコピーできます。
コレクション内の各要素を反復処理するにはどうすればよいですか? Collection の実際のタイプに関係なく、コレクション内の各要素に 1 つずつアクセスするために使用できるイテレータを返す iterator() メソッドがサポートされています。一般的な使用方法は次のとおりです。
次のようにコードをコピーします。
Iterator it = collection.iterator() // イテレータを取得します。
while(it.hasNext()) {
Object obj = it.next() // 次の要素を取得します。
}
Collection インターフェイスから派生した 2 つのインターフェイスは、List と Set です。
リストインターフェースリストは順序付けされたコレクションです。このインターフェイスを使用すると、各要素の挿入位置を正確に制御できます。ユーザーは、Java 配列に似たインデックス (配列の添え字に似たリスト内の要素の位置) を使用して、リスト内の要素にアクセスできます。
以下で説明する Set とは異なり、List では同じ要素が許可されます。
Collection インターフェイスに必要な iterator() メソッドに加えて、List には ListIterator インターフェイスを返す listIterator() メソッドも用意されています。標準の Iterator インターフェイスと比較して、ListIterator には追加できる add() メソッドやその他のメソッドがいくつかあります。要素を削除、設定し、前方または後方に移動します。
List インターフェイスを実装する一般的なクラスは、LinkedList、ArrayList、Vector、Stack です。
LinkedList クラスLinkedList は List インターフェイスを実装し、null 要素を許可します。さらに、LinkedList は、LinkedList の先頭または末尾に追加の get、remove、および insert メソッドを提供します。これらの操作により、LinkedList をスタック、キュー、または両端キューとして使用できるようになります。
LinkedList には同期メソッドがないことに注意してください。複数のスレッドが同時にリストにアクセスする場合、スレッド自体がアクセス同期を実装する必要があります。回避策の 1 つは、リストの作成時に同期されたリストを構築することです。
リスト list = Collections.synchronizedList(new LinkedList(...));
ArrayList クラスArrayList は可変サイズの配列を実装します。 null を含むすべての要素が許可されます。 ArrayList は同期されていません。
size、isEmpty、get、set メソッドの実行時間は一定です。ただし、add メソッドのコストは償却定数であり、n 個の要素を追加するには O(n) 時間がかかります。他の方法では実行時間が直線的になります。
各 ArrayList インスタンスには容量 (Capacity) があり、これは要素を格納するために使用される配列のサイズです。この容量は、新しい要素が追加されると自動的に増加しますが、増加アルゴリズムは定義されていません。多数の要素を挿入する必要がある場合は、挿入前に ensureCapacity メソッドを呼び出して ArrayList の容量を増やし、挿入効率を向上させることができます。
LinkedList と同様に、ArrayList も非同期です。
ベクタークラスVector は ArrayList に非常に似ていますが、Vector は同期されます。 Vector で作成された Iterator は ArrayList で作成された Iterator と同じインターフェイスを持ちますが、Vector は同期されているため、Iterator が作成されて使用されると、別のスレッドによって Vector の状態が変更されます (たとえば、要素の追加や削除など)。 , Iterator メソッドを呼び出すと ConcurrentModificationException がスローされるため、例外をキャッチする必要があります。
スタッククラススタックは Vector から継承し、後入れ先出しスタックを実装します。 Stack には、Vector をスタックとして使用できるようにする 5 つの追加メソッドが用意されています。基本的なプッシュ メソッドとポップ メソッド、およびピーク メソッドはスタックの先頭にある要素を取得し、空のメソッドはスタックが空かどうかをテストし、検索メソッドはスタック内の要素の位置を検出します。スタックは、作成後の空のスタックです。
インターフェイスの設定Set は、重複する要素を含まない Collection です。つまり、任意の 2 つの要素 e1 と e2 には e1.equals(e2)=false があり、Set には最大 1 つの null 要素があります。
明らかに、Set コンストラクターには、渡された Collection パラメーターに重複した要素を含めることができないという制約があります。
注意: 可変オブジェクトは注意して扱う必要があります。 Set 内の可変要素の状態が変化して Object.equals(Object)=true になると、いくつかの問題が発生します。
Map インターフェイス<BR>Map は、キーと値のマッピングを提供する Collection インターフェイスを継承しないことに注意してください。マップに同じキーを含めることはできず、各キーは 1 つの値のみをマップできます。 Map インターフェイスは、3 種類のセット ビューを提供します。Map のコンテンツは、キー セットのセット、値セットのセット、またはキーと値のマッピングのセットと見なすことができます。
ハッシュテーブルクラスHashtable は Map インターフェイスを継承し、キーと値のマッピングのハッシュ テーブルを実装します。 null 以外の任意のオブジェクトをキーまたは値として使用できます。
データを追加するには put(key, value) を使用し、データを削除するには get(key) を使用します。これら 2 つの基本操作の時間コストは一定です。
Hashtable は、初期容量と負荷率という 2 つのパラメーターを通じてパフォーマンスを調整します。通常、デフォルトの負荷係数 0.75 を使用すると、時間と空間のバランスがより良くなります。負荷率を増やすとスペースを節約できますが、それに対応する検索時間が増加し、get や put などの操作に影響します。
Hashtable を使用する簡単な例は次のとおりです。Hashtable に 1、2、および 3 を入力します。これらのキーはそれぞれ「one」、「two」、「three」です。
次のようにコードをコピーします。
ハッシュテーブル番号 = new Hashtable();
numbers.put(“1”, new Integer(1));
numbers.put(“2”, new Integer(2));
numbers.put(“three”, new Integer(3));
2 などの数値を取得するには、対応するキーを使用します。
整数 n = (整数)numbers.get(“two”);
System.out.println(“two = ” + n);
キーとして使用されるオブジェクトはハッシュ関数を計算することによって対応する値の位置を決定するため、キーとして使用されるオブジェクトはすべて hashCode メソッドと equals メソッドを実装する必要があります。 hashCode メソッドと equals メソッドは、ルート クラス Object を継承します。カスタム クラスをキーとして使用する場合、ハッシュ関数の定義に従って、2 つのオブジェクトが同じである場合、つまり obj1.equals( obj2)=true の場合、ハッシュコードは次のようになります。同じですが、2 つの異なるオブジェクトの hashCode が異なるとは限りません。この現象は競合と呼ばれ、ハッシュ テーブルの操作にかかる時間コストが増加します。 hashCode() メソッドを使用すると、ハッシュ テーブルの操作を高速化できます。
同じオブジェクトに異なる hashCode がある場合、ハッシュ テーブルの操作で予期しない結果が生じます (予期された get メソッドが null を返す)。この問題を回避するには、1 つのことだけを覚えておく必要があります。それは、equals メソッドと hashCode メソッドを同時にオーバーライドすることです。そのうちの 1 つだけを書かないでください。
ハッシュテーブルは同期です。
ハッシュマップクラスHashMap は Hashtable に似ていますが、HashMap が非同期で null、つまり null 値と null キーを許可する点が異なります。ただし、HashMap をコレクションとして扱う場合 (values() メソッドはコレクションを返すことができます)、その反復サブオペレーションの時間オーバーヘッドは HashMap の容量に比例します。したがって、反復操作のパフォーマンスが非常に重要な場合は、HashMap の初期容量を高く設定しすぎたり、負荷率を低く設定しすぎたりしないでください。
WeakHashMap クラスWeakHashMap は、キーへの「弱参照」を実装する改良された HashMap で、キーが外部から参照されなくなった場合、そのキーは GC によってリサイクルできます。
まとめ<BR>スタックやキューなどの操作が関係する場合は、List の使用を検討する必要があります。要素を迅速に挿入および削除する必要がある場合は、LinkedList を使用する必要があります。要素への高速なランダム アクセスが必要な場合は、ArrayList を使用する必要があります。
プログラムがシングルスレッド環境にある場合、またはアクセスが 1 つのスレッドでのみ実行される場合は、より効率的な非同期クラスを検討してください。複数のスレッドが同時にクラスを操作する可能性がある場合は、同期クラスを使用する必要があります。
ハッシュ テーブルの操作には特に注意してください。キーとして使用されるオブジェクトは、equals メソッドと hashCode メソッドを正しくオーバーライドする必要があります。
将来 ArrayList を LinkedList に置き換える必要がある場合に、クライアント コードを変更する必要がないように、ArrayList の代わりに List を返すなど、実際の型ではなくインターフェイスを返すようにしてください。これは抽象化のためのプログラミングです。