マップインターフェイス
- マップとコレクションは並んで存在します。マッピング関係でデータを保存するために使用されます:key-value
- マップのキーと値はどちらも、任意の参照型データにすることができます
- MapのキーはSetに格納され、繰り返しは許可されません。つまり、同じMapオブジェクトに対応するクラスはhashCode()メソッドとequals()メソッドをオーバーライドする必要があります。
- キーと値の間には一方向の1対1の関係があります。つまり、valueMapインターフェイスの一意で明確な共通の実装クラスは、指定されたキー(HashMap、TreeMap、LinkedHashMap、およびProperties)を通じて常に見つけることができます。その中で、HashMapは、最も頻繁に使用されるMapインターフェースを備えた実装クラスです。
Mapインターフェースの一般的な方法:
方法 | 説明 |
---|---|
オブジェクトput(オブジェクトキー、オブジェクト値) | 指定されたKey-Valueを現在のマップオブジェクトに追加(または変更)します |
void putAll(Map m) | m単位のすべてのキーと値のペアを現在のマップに格納します |
オブジェクト削除(オブジェクトキー) | 指定されたキーのキーと値のペアを削除し、値を返します |
void clear() | 現在のマップのすべてのデータをクリアします |
オブジェクトget(オブジェクトキー) | 指定されたキーに対応する値を取得します |
ブールcontainsKey(オブジェクトキー) | 指定されたキーを含めるかどうか |
ブールcontainsValue(オブジェクト値) | 指定した値を含めるかどうか |
int size() | マップ内のキーと値のペアの数を返します |
ブールisEmpty() | 現在のマップが空かどうかを確認します |
boolean equals(Object obj) | 現在のマップとパラメータオブジェクトobjが等しいかどうかを確認します |
keySet()を設定します | すべてのキーのセットを返します |
コレクションvalues() | すべての値で構成されるコレクションを返します |
entrySet()を設定します | すべてのキーと値のペアのSetコレクションを返します |
Map実装クラスの1つ:HashMap
- HashMapは、最も頻繁に使用されるMapインターフェースを備えた実装クラスです。
- nullキーとnull値が許可されます。HashSetと同様に、マッピングの順序は保証されません。
- すべてのキーで構成されるセットは、セット:無秩序で繰り返し不可能です。したがって、キーが配置されているクラスを書き直す必要があります:equals()およびhashCode()
- すべての値で構成されるコレクションは、コレクション:無秩序で繰り返し可能です。したがって、値が配置されているクラスは次のように書き直す必要があります。equals()
- Key-Valueはエントリを構成します
- すべてのエントリのセットはセットです:無秩序で繰り返し不可
- 2つのキーが等しいとHashMapが判断するための基準は、2つのキーがequals()メソッドを介してtrueを返し、hashCode値も等しいことです。
- 2つの値が等しいとHashMapが判断するための基準は次のとおりです。2つの値はequals()メソッドを介してtrueを返します。
HashMapストレージ構造
HashMapソースコードの重要な定数
** DEFAULT_INITIAL_CAPACITY **:HashMapのデフォルト容量、16
** MAXIMUM_CAPACITY **:HashMapでサポートされる最大容量、2 ^ 30
DEFAULT_LOAD_FACTOR:HashMapのデフォルトの負荷係数は0.75です
TREEIFY_THRESHOLD:バケット内のリンクリストの長さがデフォルト値より長く、赤黒木に変換されます8
UNTREEIFY_THRESHOLD:バケットの赤黒木に格納されているノードがデフォルト値よりも小さく、リンクリストに変換されます
MIN_TREEIFY_CAPACITY:バケット内のノードがツリー化されている場合の最小ハッシュテーブル容量。(バケット内のノード数が多すぎて赤黒木になる必要がある場合、ハッシュテーブルの容量がMIN_TREEIFY_CAPACITY未満の場合は、この時点でサイズ変更操作を実行する必要があります。MIN_TREEIFY_CAPACITYの値は少なくともTREEIFY_THRESHOLDの4倍。)64
テーブル:ストレージ要素の配列。常に2のn乗。
entrySet:特定の要素のセットを格納します
サイズ:HashMapに保存されているキーと値のペアの数
modCount:HashMapが展開され、構造が変更された回数。
しきい値:容量拡張の臨界値、=容量*曲線因子16 * 0.75 = 12
loadFactor:フィルファクター
JDK1.8より前のソースコード分析
HashMapの内部クラス:ノード
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
...
}
HashMapの内部ストレージ構造は、実際には配列とリンクリストの組み合わせです。HashMapをインスタンス化すると、システムは、Capacityの長さのEntry配列を作成します。この長さは、ハッシュテーブルではCapacityと呼ばれます。この配列に要素を格納できる場所は、「バケット」(バケット)と呼ばれ、各バケットには独自のインデックスがあり、システムはインデックスに従ってバケット内の要素をすばやく見つけることができます。
各バケットには要素、つまりEntryオブジェクトが格納されますが、各Entryオブジェクトは、次の要素を指す参照変数を運ぶことができます。したがって、バケットでは、Entryチェーンを生成できます。そして、新しく追加された要素は、リンクリストの先頭として使用されます。
要素を追加するプロセス:
entry1(key、value)をHashMapに追加するには、最初にentry1のキーのハッシュ値を計算する必要があります(キーが配置されているクラスのhashCode()に従って計算されます)。ハッシュ値が処理された後、基になるEntry []配列で取得されます。格納される場所i。位置iに要素がない場合、entry1が直接追加されます。entry2がすでに位置iに存在する場合(またはリンクリストにentry3、entry4がある場合)、ループメソッドを使用してentry1のキーを他のエントリと順次比較する必要があります。ハッシュ値が互いに異なる場合、直接加算は成功します。
ハッシュ値が異なる場合は、2つが等しいかどうかを引き続き比較します。戻り値がtrueの場合、entry1の値は、equalsがtrueであるエントリの値を置き換えるために使用されます。再度トラバースした後、すべての等しい戻り値がfalseであることが判明した場合でも、entry1を正常に追加できます。entry1は、元のエントリ要素を指します。
HashMapの拡張
HashMapに要素が増えると、配列の長さが固定されるため、ハッシュの競合の可能性がますます高くなります。したがって、クエリの効率を向上させるには、HashMap配列を展開する必要があります。HashMap配列の展開後、最もパフォーマンスを消費するポイントが表示されます。元の配列のデータは、の位置で再計算する必要があります。新しい配列と配置されたGoin、これはサイズ変更です。
では、HashMapはいつ拡張されるのでしょうか?
HashMapの要素数が配列のサイズ(配列の数のサイズではなく、配列の全長)* loadFactorを超えると、配列が展開されます。loadFactorのデフォルト値(DEFAULT_LOAD_FACTOR)は0.75です。これは妥協値です。つまり、デフォルトでは、配列サイズ(DEFAULT_INITIAL_CAPACITY)は16ですが、HashMapの要素数が16 * 0.75 = 12を超えると(この値はコード内のしきい値であり、クリティカル値とも呼ばれます)、配列のサイズは2 * 16 = 32に拡張されます。つまり、2倍になり、配列内の各要素の位置が再計算されます。これは非常にパフォーマンスを消費する操作であるため、要素の数をすでに予測している場合HashMapでは、プリセット要素の数によってHashMapのパフォーマンスを効果的に向上させることができます。
JDK1.8以降のソースコード分析
HashMapの内部ストレージ構造は、実際には配列+リンクリスト+ツリーの組み合わせです。HashMapをインスタンス化すると、initialCapacityとloadFactorが初期化されます。マッピング関係の最初のペアを配置すると、システムはinitialCapacityの長さのノード配列を作成します。この長さはハッシュテーブルでCapacityと呼ばれます。要素を配置できる場所配列に格納されているものは「バケット」と呼ばれ、各バケットには独自のインデックスがあり、システムはインデックスに基づいてバケット内の要素をすばやく見つけることができます。
各バケットには要素、つまりノードオブジェクトが格納されますが、各ノードオブジェクトは次の要素を指すために参照変数を運ぶことができます。したがって、バケット内でノードチェーンを生成できます。TreeNodeオブジェクトの場合もあります。各TreeNodeオブジェクトは、左右に2つのリーフノードを持つことができます。したがって、バケット内でTreeNodeツリーを生成できます。新しく追加された要素は、リンクリストの最後、またはツリーのリーフノードとして機能します。
では、HashMapはいつ拡張され、ツリー型になるのでしょうか。
HashMapの要素数が配列のサイズ(配列内の数のサイズではなく、配列の全長)* loadFactorを超えると、配列が展開されます。loadFactorのデフォルト値(DEFAULT_LOAD_FACTOR)は次のとおりです。 0.75、これは妥協値です。つまり、デフォルトでは、配列サイズ(DEFAULT_INITIAL_CAPACITY)は16ですが、HashMapの要素数が16 * 0.75 = 12(この値はコード内のしきい値であり、クリティカル値とも呼ばれます)を超えると、 arrayのサイズは2 * 16 = 32に拡張されます。つまり、2倍になり、配列内の各要素の位置が再計算されます。これは非常にパフォーマンスを消費する操作であるため、 HashMap、次にプリセット要素の数により、HashMapのパフォーマンスを効果的に向上させることができます。
HashMapのチェーンの1つにあるオブジェクトの数が8に達すると、容量が64に達しない場合、HashMapが最初に展開されます。64に達すると、チェーンはツリーになり、ノードタイプはNodeからTreeNodeタイプに変更されます。もちろん、マッピング関係が削除された後、次にサイズ変更メソッドがツリー内のノードの数が6未満であると判断した場合、ツリーもリンクリストに変換されます。
マッピング関係のキーを変更できますか?回答:
マッピング関係を変更したり、キーのハッシュ値をHashMapに保存したりしないでください。これにより、検索するたびに各エントリまたはノード(TreeNode)のハッシュ値を再計算する必要がなくなります。マッピング関係をマップに配置し、キー属性を変更すると、この属性がハッシュコード値の計算に関与し、一致が失敗します。
概要:以前の変更と比較したJDK1.8:
- HashMap map = new HashMap(); //デフォルトでは、長さ16の配列は最初に作成されません
- map.put()が初めて呼び出されると、長さ16の配列が作成されます。
- 配列はノードタイプであり、jdk7ではエントリタイプと呼ばれます。
- リンクリスト構造を形成する場合、新しく追加されたキーと値のペアは、リンクリストの最後にあります(7つのアップと8つのダウン)
- 配列の指定されたインデックス位置でのリンクリストの長さが> 8で、マップ内の配列の長さが> 64の場合、このインデックス位置のすべてのキーと値のペアは赤黒木に格納されます。
jdk7と比較すると、jdk8は基礎となる実装が異なります。
- new HashMap():最下層は長さ16の配列を作成しませんでした
- jdk 8の基礎となる配列は、Entry []ではなくNode []です。
- put()メソッドが初めて呼び出されると、最下層は長さ16の配列を作成します
- jdk7の基本構造には、配列+リンクリストのみがあります。jdk8の基礎となる構造:配列+リンクリスト+赤黒木。
- リンクリストを作成する場合、7つの浮き沈みがあります(jdk7:新しい要素は古い要素を指します。jdk8:古い要素は新しい要素を指します)
- リンクリスト形式の配列のインデックス位置にあるデータの数が8を超え、現在の配列の長さが64を超える場合、このインデックス位置のデータは赤黒木ストレージを使用するように変更されます。
面接の質問:
HashMapのput / getメソッドに関する知識について教えてください。理解できたら、HashMapの拡張メカニズムについて話してください。デフォルトのサイズはいくつですか?負荷率(または充填率)とは何ですか?スループットのしきい値(またはしきい値、しきい値)とは何ですか?
負荷係数値がHashMapに与える影響は何ですか?
- 負荷率のサイズによって、HashMapのデータ密度が決まります。
- 負荷率が高いほど、密度が高くなり、衝突の可能性が高くなり、配列内のリンクリストが長くなります。これにより、クエリまたは挿入中の比較の数が増え、パフォーマンスが低下します。
- 負荷率が小さいほど、拡張のトリガーが容易になります。データ密度が小さいほど、衝突の可能性が低くなり、配列内のリンクリストが短くなり、クエリと挿入の比較回数が少なくなり、高くなります。パフォーマンス。ただし、一定量のコンテンツスペースが無駄になります。さらに、頻繁な拡張もパフォーマンスに影響します。より大きなスペースを初期化することをお勧めします。
- 他の言語での参照と研究の経験によると、負荷率は0.7〜0.75に設定されていると見なされます。このとき、平均検索長は一定に近くなります。
マップ実装カテゴリ2:LinkedHashMap
- LinkedHashMapはHashMapのサブクラスです
- HashMapのストレージ構造に基づいて、二重にリンクされたリストのペアを使用して、要素を追加する順序を記録します。
- LinkedHashSetと同様に、LinkedHashMapはMapの反復を維持できます
- 順序:反復順序は、Key-Valueペアの挿入順序と一致しています
LinkedHashMapの内部クラス:エントリ
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after;//能够记录添加的元素的先后顺序
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}
マップ実装カテゴリ3:TreeMap
- TreeMapがキーと値のペアを格納する場合、キーと値のペアに従ってソートする必要があります。TreeMapは、すべてのKey-Valueペアが整然とした状態にあることを保証できます。
- TreeSetの最下層は、赤黒木構造を使用してデータを格納します
- TreeMapのキーの並べ替え:
- 自然順序付け:TreeMapのすべてのキーはComparableインターフェイスを実装する必要があり、すべてのキーは同じクラスのオブジェクトである必要があります。そうでない場合、ClasssCastExceptionがスローされます。
- カスタムソート:TreeMapを作成するときに、TreeMap内のすべてのキーのソートを担当するComparatorオブジェクトを渡します。現時点では、比較可能なインターフェイスを実装するためにマップキーは必要ありません。
- TreeMapが2つのキーが等しいと判断するための基準:2つのキーはcompareTo()メソッドまたは
compare()メソッドを介して0を返します。
マップ実装カテゴリ4:ハッシュテーブル
- Hashtableは、JDK1.0によって提供される古いMap実装クラスです。HashMapとは異なり、Hashtableはスレッドセーフです
- Hashtableの実装原理はHashMapと同じであり、機能も同じです。最下層はハッシュテーブル構造を採用しており、クエリ速度が速いため、相互に利用できる場合が多い。
- HashMapとは異なり、Hashtableではキーと値としてnullを使用できません
- HashMapと同様に、HashtableはKey-Valueペアのシーケンスを保証できません。
- 2つのキーが等しく、2つの値が等しいとHashtableが判断するための基準は、HashMapと一致しています
マップ実装カテゴリ5:プロパティ
- PropertiesクラスはHashtableのサブクラスであり、このオブジェクトはプロパティファイルを処理するために使用されます
- プロパティファイルのキーと値は両方とも文字列タイプであるため、プロパティのキーと値は両方とも文字列タイプです
- データにアクセスするときは、setProperty(文字列キー、文字列値)メソッドとgetProperty(文字列キー)メソッドを使用することをお勧めします
Properties pros = new Properties();
pros.load(new FileInputStream("jdbc.properties"));
String user = pros.getProperty("user");
System.out.println(user);