HashMapはどのように実装されているので、キーと値をすばやくマッピングできますか?
1.概要
HashMapはキーで要素を検索できます.
キーと値のリストがあるとします.必要なキーを検索するにはどうすればいいですか?
2号法では時間複雑度は平均O(1)であった.また,HashMapは第2の方法によって実現される.では、HashMapがどのように機能しているかを詳しく見てみましょう.
1.2キーの適当な数は?
では、何個の要素があるか分からない場合、何個のキーを生成する必要があるのでしょうか.
例えば、身長はアルファベットの小文字にすぎないと仮定します.この場合、26個のキーリストだけで十分です.「d」キーを持つエンティティにアクセスするには、位置が4の場合、直接検索できます.
ただし、ユーザーが次のように宣言する場合を考えてみましょう.
HashMap<Integer, String> map = new HashMap<>();
Integer.MAX VALUE値は2147483647です.いくらデータが多くても、整数の最値まですべての要素を含めることはできません.この場合、割り当てられたメモリの多くの部分が未使用になり、スペースが浪費され、検索効率が低下します.したがって、HashMapは要素をbucketに格納し、これらのbucketの数を容量と呼ぶ.bucketは韓国語でバケツに訳して、要素を入れたバケツだと思うと気持ちがいいです
Mapコレクションに値を入れる場合、キーのhashCode()メソッドは、格納された値のパケットを決定するために使用されます.そして、値を検索するために、HashMapはHashCode()を用いて同じようにパケットを計算する.次に、bucketでオブジェクトを検索し、キーのequals()メソッドを使用して正しい一致するアイテムを検索します.
整理すると、HashMapはすべての要素を巡回するのではなく、キーに基づいて値の位置を計算します.
1.3ハッシュ競合
要素を入れるときに必要な数だけ必要で、検索時にも直接キーを検索するので、どの条件でも時間複雑度はO(1)である可能性がありますが、最悪の場合、時間複雑度はO(N)である可能性があります.
デフォルトでは、同じキーには同じハッシュが必要ですが、異なるキーには同じハッシュがある場合があります.
異なるキーで同じハッシュである場合、そのキーに属する値は同じbucketに格納されます.
bucket内部では、値がListに格納され、すべての要素が巡回検索されます.このときの時間的複雑度はO(N)である.Java 8から、1つのbucketに8つ以上の値が含まれている場合、リストからバランスツリーに変更され、時間複雑度はO(logn)となる.
Javaハッシュ競合の詳細については、次のWebサイトを参照してください.
( https://d2.naver.com/helloworld/831311 )
2.内部実施HashMap
原理を理解した以上、Javaコードがジェネレータと主な方法をどのように実現しているかを見てみましょう.
2.1ジェネレータ
次はJavaのHashMapクラスの内部コードです.
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
this.threshold = tableSizeFor(initialCapacity);
}
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR;
}
public HashMap(Map<? extends K, ? extends V> m) {
this.loadFactor = DEFAULT_LOAD_FACTOR;
putMapEntries(m, false);
}
コンストラクション関数のパラメータにはinitialCapcityとloadFactorがあります.2.1.1 initialCapacity
ブラケットの初期容量のパラメータを設定します.基本初期容量は16です.
ちなみに、HashMapは複数の値のbucketを阻止するために、bucketの75%が空でなければcapsityが自動的に倍増します.
2.1.2 load factor
これは、HashMapのパケット容量を増加させるパーセント値です.
デフォルト値は0.75 fであり、容量が75%に達すると再ハッシュが発生する.
Rehashing?
パケットの容量がしきい値に達すると、JavaはRehashingと呼ばれる容量を2倍に増やします.
ex) 24, 25, 26 ...
2.2方法
HashMapには多くの方法があるが,主に検索と挿入に用いられるput()とget()の方法を重点的に紹介する.その他の方法はjava公式APIドキュメントを参照してください.
リンク:https://docs.oracle.com/javase/8/docs/api/java/util/HashMap.html
2.2.1 put()メソッド
以下にputメソッドを実装するJavaコードを示す.
V put(K key, V value);
次のコードはサンプルコードです.元のインスタントラーメンキーと値を追加すると、キーオブジェクトのhashCode()が呼び出されて初期ハッシュ値が検索されますが、次の例のコードで再定義されたhashCode()は、メソッド呼び出しの出力のみを表示するように記述されます.public class CustomKey{
private int id;
//Getter, Setter, Constructor
@Override
public int hashCode() {
System.out.println("hashCode Method called");
return id;
}
}
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
CustomKey key = new CustomKey(1);
Map<CustomKey, String> map = new HashMap<>();
map.put(key, "value");
}
}
main関数を実行すると、hashCode()メソッドというhashCodeメソッドが出力されます.次の方法はhash()方法です.この方法は、初期ハッシュ値によって最終ハッシュ値を計算する.この最終ハッシュ値は、内部配列のインデックスまたはパケット位置になります.メソッドの実装部分は次のとおりです.
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
返された値から、最終的なハッシュ値は、キーオブジェクトのhashCodeのみを使用して計算されることがわかります.次に、put関数の内部で、最終ハッシュ値は次のようになります.public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
内部putVal法の最初のパラメータhash(key)は最終的なハッシュ値である.我々はkeyを用いてhash値を計算したが,方法の2番目のパラメータが再びkeyを用いたのはhashマッピングがキーと値をbucket位置にマッピングするためである.これはEntryオブジェクトとして格納されているためです.
これもJavaのCollectionフレームワークがMapを実現しなかった理由です.
Mapは他の集合のように個々の要素を正確に格納するのではなく,キー値対の集合を格納するため,AddなどのCollectionインタフェースの方法はMapでは意味がない.
2.2.2 get() method
保存されたオブジェクトを検索するには、そのオブジェクトに格納されている鍵を知る必要があります.get()もput()と同じハッシュ操作を実行する.
オブジェクトのhashCode()メソッドが呼び出され、初期ハッシュ値が取得されます.次にhash()メソッドを呼び出して最終的なhash値を取得し、その値によってパケットの場所を検索します.
2.2.3許容空
HashTableとは異なり、HashMapではキーと値がnullになります.put()メソッドが実行中にnullキーを発見すると、最終的なハッシュ値は自動的に0になります.これは、HashMapの最初の要素になることを意味します.キーがnullの場合、値は0になっているのでhash()操作は発生しないため、Null PointerExceptionは発生しません.
Reference
この問題について(HashMapはどのように実装されているので、キーと値をすばやくマッピングできますか?), 我々は、より多くの情報をここで見つけました https://velog.io/@dailyzett/HashMapテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol