私を信じてください、あなたは良くなるでしょう!
備考:jdkバージョンは1.7で、HashMapを初めて知りました(1.8以上のHashMapを取得するために引き続き使用します)
目次
1. HashMapとは何ですか?いつHashMapを選択する必要がありますか?
3. HashMapとHashTableの類似点と相違点は何ですか?
1. HashMapとは何ですか?いつHashMapを選択する必要がありますか?
コンテナーと言えば、Javaのオブジェクトストレージコンテナー、ArrayList、LinkedList、HashSetなどを思い浮かべるでしょう。これらのコンテナーと比較すると、HashMapはポインティング関係の追加レイヤーとして理解でき、指定されたキーを使用して指定された値を見つけます。
類推するには:
現在、従業員情報を格納するために使用されるJava Beanがあります。フィールドには(従業員名、従業員の年齢、従業員の身長、従業員の体重、従業員の教育レベルなど)が含まれます。私は人事マネージャーであり、従業員を追加する必要があります。情報整理して上司に送ってください。
アイコン:
★ 質問:
このとき、2人が同じ名前の場合はどうしたらいいか考えなければなりませんが、誰の情報を探していますか?以前の情報は上書きされますか?HashMapのデータ構造とその動作原理について学ぶために質問をしてください。
2. HashMapデータ構造とその動作原理?
2.1データ構造
HashMapのデータ構造は、配列+リンクリストです。リンクリストのノードにはEntryオブジェクトが格納され、各Entryオブジェクトには4つの属性(ハッシュ、キー、値、次)が格納されます。
写真が表示されます:
3つの文で、そのデータ構造を説明します。
- 全体が配列です。
- 配列の各位置はリンクリストです。
- リンクリストの各ノードの値は、格納するオブジェクトです。
2.2動作原理
まず、HashMapを初期化し、パラメーター化された構造とパラメーターなしの構造を提供します。パラメーターなしの構造では、コンテナーのデフォルトの配列サイズ は16で、負荷係数loadFactorは0.75です。コンテナーのしきい値(yu)値はinitialCapacity * loadFactorであり、デフォルトではしきい値は16 * 0.75 = 12です。しきい値の使用については後で説明します。
次に、ここでは研究のために PUT メソッドを使用します。
ステップ1: HashMap自体が提供するハッシュアルゴリズムを使用して、現在のキーのハッシュ値を計算します
ステップ2:計算されたハッシュ値を使用してindexForメソッドを呼び出し、現在のオブジェクトを配列のどこに格納するかを計算します
手順3:サイズが現在のしきい値に達しているかどうかを判断し、達していない場合は続行します。しきい値に達した場合は、最初に配列を拡張し、配列の長さを元の2倍に拡張します。
>注意:サイズは、配列の長さではなく、現在のコンテナ内の既存のエントリの数です。
ステップ4:現在の対応するハッシュ、キー、および値をエントリにカプセル化し、配列に移動して、現在の位置に要素があるかどうかを確認します。ない場合は、この位置に配置します。すでにリンクリストがある場合は、この位置で、リンクリストをトラバースします。リンクリストのノードのキーが現在のキーと等しいかどうかを比較した後、結果がtrueの場合、元のノードの値が返され、現在の新しい値は次のようになります。リンクリストがトラバースされた場合、現在のキーと等しいキーは見つかりません。それがtrueの場合、現在のリンクリストの開始ノードにカプセル化された新しいエントリの次のポイントをポイントします。 、現在のノードは、リンクリストの最初の位置にあります。簡単に言うと、最初に前後に移動します。
OK!これで、現在のKey-Valueがコンテナーに格納されました。
なぜ私はPUT メソッドについて話すことを選んだの ですか?
そのため PUTは、 HashMapを操作するための最も基本的な操作で、あなたが理解した後のメカニズム PUTを 使用すると、他のAPIメソッドのソースコードを見たとき、あなたは驚かれることでしょう。あなたは、この最初の知識を持つのHashMapの他の方法を探索することができ、そして必要になります間違いなく突然光を見る。
拡張メカニズム:
HashMapのは、それだけだろう「怠惰な拡張」を使用して 判断PUT、その後拡大を。
- 配列の長さを元の2倍に拡張します
- 元の配列の要素を新しい配列に戻します
展開するたびに、新しい配列内の元のエントリの位置を再計算する必要があることに注意してください。配列が展開され、配列内のエントリの位置が変更されたのはなぜですか。そこで、位置を計算するためのindexForメソッドについて考えます。なぜですか?メソッドのソースコードを次のように抽出しました。
-
static int indexFor(int h, int length) { // h 为key 的 hash值;length 是数组长度
-
return h & (length-1);
-
}
ソースコードによると、要素の位置は配列の長さに関連しています。拡張後に配列の長さが変更されたため、要素の位置を変更する必要があります。HashMapは&操作を使用して要素の位置を計算します。この操作がわからない場合は、ここで簡単な例を示します。
高エネルギー:HashMapがこのメソッドを使用して配列内の位置を計算するのはなぜですか?
私たちの潜在意識によると、モジュラスを取るだけで十分です。hashMapのAND演算は、主にコンピューティングパフォーマンスを向上させることです。これにより、新しい質問が発生します。なぜAND演算に長さ-1を使用するのですか?ハッシュマップを初期化するとき、配列の長さは2の累乗でなければなりません(パラメーターグループの長さが手動で奇数nの場合、hashMapは自動的に長さを2のnの最も近い累乗に変換します。この方法でのみ、h&(長さ-1)の値はh%長さ計算の結果と同じになります。これがその理由です。さらに、長さが2の整数乗の場合、長さ1の結果は下位ビットですべて1になります。これは、後続の拡張の良い準備です。これについてはここでは説明しません。最初に意味を理解しましょう。 。
検証する単体テストを書いてみましょう。
-
public static void main(String[] args) {
-
/**
-
* 定义数组长度为2的整次幂,2^4
-
*/
-
int length = 16;
-
/**
-
* 定义key,并计算k的hash值
-
*/
-
String k = "China";
-
int h = k.hashCode();
-
/**
-
* 分别使用两种方式计算在数组中的位置
-
*/
-
int index1 = h % length;
-
int index2 = h & (length - 1);
-
/**
-
* 验证结果
-
*/
-
System.out.println(index1 == index2);
-
/**
-
* 结果为 true
-
*/
-
}
-
public static void main(String[] args) {
-
/**
-
* 假设数组长度不是2的整次幂,2^4-1
-
*/
-
int length = 15;
-
/**
-
* 定义key,并计算k的hash值
-
*/
-
String k = "China";
-
int h = k.hashCode();
-
/**
-
* 分别使用两种方式计算在数组中的位置
-
*/
-
int index1 = h % length;
-
int index2 = h & (length - 1);
-
/**
-
* 验证结果
-
*/
-
System.out.println(index1 == index2);
-
/**
-
* 打印结果:false
-
*/
-
}
知識を身につける:
8&6 = 0を計算するプロセスは次のとおりです。
-
1 0 0 0 // 8的二进制数
-
& 0 1 1 0 // 6的二进制数
-
___________ // 运算规则:该位置上有一个是0 结果就是0
-
0 0 0 0 // 二进制数计算结果
(1)で述べた問題を覚えていますか?あなたは答えを知っていますか?
回答:HashMapで同じ等しい2つのキーの場合、後で入力されるキーの値のみがコンテナーに保持されます。質問に:最初にルーシーの情報を保存し、次に別のルーシーがありました。このとき、ルーシーを保存します。コンテナには2番目のルーシーの情報が保持されます。この場合、List <T>を次のように使用することを検討できます。 valueには、同じ名前の従業員情報をリストに格納するか、同じ名前の従業員に番号を付けて、各キーが一意になるようにします。
3. HashMapとHashTableの類似点と相違点は何ですか?
- 両方のストレージ構造と競合解決方法は同じです。
- 容量を指定しないHashTableのデフォルトの容量は11ですが、HashMapは16です。Hashtableでは、基になる配列の容量を2の整数乗にする必要はありませんが、HashMapでは2の整数乗が必要です。
- HashTableのキーも値もnullにすることはできませんが、 HashMapのキーと値の両方をnullにすることはできます(1つのキーのみをnullにすることができ、複数の値をnullにすることができます)。ただし、Hashtableにput(null、null)と同様の操作がある場合、キーと値はObjectタイプであるため、コンパイルも成功しますが、実行時にNullPointerExceptionがスローされます。
- Hashtableを拡張すると、容量は+1の2倍になり、HashMapを拡張すると、容量は2倍になります。
- Hashtableはハッシュ値を計算し、キーのhashCode()を直接使用し、HashMapはキーのハッシュ値を再計算します。Hashtableがハッシュ値に対応する位置インデックスを計算するとき、%演算を使用 し、HashMapが位置インデックスを見つけるとき、使用 &操作。
4. HashMapを最適化する方法は?
HashMapを初期化するときに、配列の容量と負荷率のサイズをカスタマイズできます。したがって、HashMapの最適化は、これら2つの属性から始まります。ただし、ビジネスに必要なサイズを正確に決定できない場合は、デフォルト値を使用してください。そうしないと、手動構成が不適切になると、効果が逆効果になります。
しきい値=(int)(容量* loadFactor);
しきい値=容量X負荷率;
初期容量のデフォルトは16、負荷係数(loadFactor)のデフォルトは0.75です。マップを展開した後、しきい値を再計算する必要があります。要素の数が新しいしきい値よりも大きい場合、マップは自動的に再び展開されます。デフォルト値、たとえば、threshold = 16 * 0.75 = 12の場合、要素の数が12を超えると、容量が拡張されます。オブジェクトを配置する前に、残りの4つの配列位置を拡張する必要があるため、スペースなので、時間とスペースの間の妥協点を考慮する必要があります。
loadFactorが大きすぎると、マップ内の配列の使用率が高くなり、内部エントリチェーンが形成される可能性が非常に高くなり、検索速度に影響します。
loadFactorが小さすぎて、マップ内の配列の使用率が低いですが、エントリの内部チェーン、エントリを生成しないか、非常に短いチェーンを生成しないため、検索速度が向上しますが、より多くのメモリを消費します。実際のハードウェア上プログラムの環境と実行状態は、loadFactorを調整するために使用されます。
したがって、HashMapを適切に初期化するようにしてください
あなた自身とあなたの周りの人々の生活を変えてみてください。
特にこの記事がお役に立てば幸いです。転載の出典を教えてください。議論と交換のためにメッセージを残してくれてありがとう。