HashMap


HashMap
Mapを実装する典型的なカテゴリ.
鍵を保存するときhash関数を使用します.HashMapと呼ばれます.Valueを保存すると,キー値をハッシュ処理し,処理後のハッシュコードを圧縮してインデックスを生成する.entryは対応するインデックスに格納されます.特徴をまとめると以下のようになります.
特長
  • Mapの性質を持つため、Key、Value pair構造を持つEntryクラスを使用してデータを管理する.
  • 鍵はゲストでなければなりません.これは、データの格納とアクセス時に、オブジェクトのhashCode()とequals()によって鍵が一致するかどうかを検証するためです.基本資料型で鍵が指定されている場合は、Wrapperclassで箱詰めされます.
  • 論理で同じキーの値を繰り返し入力すると、最後に入力したキーと等しい値に更新されます.すなわち、重複鍵の使用は許されない.
  • nullは、
  • KeyおよびValueで参照できます.しかし、必要でなければ避けるべきだ.
  • は、アレイのMapに基づいている.データ取得時にhashを用いてアクセスするため,単純記憶/検索を用いて計算する場合,平均O(1)の時間的複雑さを持つ.このため、格納されたデータに順次アクセスして検索するArrayListよりも検索速度が速い.検索するデータがソートされている場合、パフォーマンスはO(n)に向上し、バイナリ検索を行う場合、パフォーマンスはO(logn)に向上します.HashMapの最大の特徴と言えるでしょう.
  • ですが、各エントリの入力順序は覚えていません.したがって、データ入力の順序が重要であれば、LinkedHashMapを使用することを選択できます.
  • は内部が同期していないため、HashMapの構造的な変更(値の変更など)を防ぐために、マルチスレッド環境では外部で同期または収集を宣言する必要があります.synchronizedMap(new HashMap()); に示すように、同期設定を行うことが望ましい.(同期の概念しか知らないので、今はこれらしか書かれていません)
  • TL;DR
  • ハッシュ関数のMapを使用します.
  • 簡単なデータナビゲーション、反復はArrayListよりずっと速い.
  • Keyを繰り返すことはできません.
  • HashMapコンセプト
    HashMapを含むMap集合を実装するクラスの大部分は以下の概念から構成される.
    Bucket
    HashMapは前述したようにアレイベースのMapであり、各アレイ内のノードをbucketと呼ぶ.entry、格納されている要素をbucketと見なせばいいです.
    Capacity
    保存できるbucketの数.HashMapの容量だと思いますHashMapをデフォルトジェネレータとして作成した場合、デフォルト容量は16です.HashMapを生成する場合、ジェネレータのパラメータで容量を設定できます
    Threshold
    しきい値今はHashMapがストレージスペースを増やす必要がある時です.load factor*capacityで計算できます.loadfactorは、値をいつ増加すべきかを決定する単位です.デフォルト設定は0.75 fで、容量の75%を表します.
    たとえば、HashMapをデフォルトジェネレータに設定すると、容量は16、負荷係数は0.75 f、しきい値は16*0.75=12になります.すなわち、このHashMapは16個のエントリを格納することができ、mapのサイズが12個に達するとHashMapのサイズが大きくなる.大きさは2倍近く大きくなります.したがって、16のサイズは32に大きくなります.
    Rehashing
    HashMapがしきい値に達して大きくなると、内部ですべてのbucketが再調整されます.これは構造的な変更が発生することを意味する.load factorを低く設定することで複雑さを低減できますが、再調整が頻繁に発生します.rehashing自体のリソース消費が比較的大きいため、HashMapに格納されているデータが予測可能であれば、容量を適切に向上させ、これらの問題をある程度解決することができる.
    HashMap methods, constructor
    constructor
    コンストラクション関数には、基本コンストラクション関数と、capacity、load factorをパラメータとするコンストラクション関数、map実装クラスをパラメータとするコンストラクション関数があります.
    
    HashMap<String, String> bandNameAndCountry = new HashMap<>;
    bandName.put("radiohead", "Britain");
    bandName.put("interpol", "America");
    
    HashMap<String, String> bandNameAndCountry2 = new HashMap<>(bandNameAndCountry);
    
    System.out.println(bandNameAndCountry.equals(bandNameAndCountry2)); // true
    	
    method
    Mapのすべての方法を実現した.put、get、remove、containValueなどが利用できます.
    getOrDefault
    JDK 1.8から追加された機能.キーで値を取得できますが、キーが存在しないか、キーのVauleにマッピングされていない場合は、default値を指定してリカバリできます.
    HashMap<String, String> hashMap = new HashMap<>();
    hashMap.put("hungry", "yes");
    System.out.println(hashMap.getOrDefault("thirsty", "yeah")); // yeah
    
    HashMapは、後のentrySet()やkeySet()、values()などで反復できます.
    HashMapの基本利用率
    HashMapのエントリは様々な方法で出力できます.
    hashMapの例
    HashMap<String, String> hashMap = new HashMap<String, String>();
    hashMap.put("food1", "hamburger");
    hashMap.put("food2", "pizza");
    hashMap.put("food3", "ramen");
    hashMap.put("food4", "ramen");
    entrySet()
    これはMapのentryをSet Viewとして作成して反復する方法です.
    for(Map.Entry<String, String> entries : hashMap.entrySet()) {
    	System.out.println("key :"+ entries.getKey() + " value : "+entries.getValue()); 
    }
    
    //key :food1 value : hamburger
    //key :food3 value : ramen
    //key :food2 value : pizza
    //key :food4 value : ramen
    
    前述したように、HashMapでは、データの格納順序は重要ではないため、反復時に異なる順序が発生する可能性がある.
    keySet()
    キーのみがSet Viewに変換され、対応するキー反復get()を通過する.
    		
    for(String s : hashMap.keySet()) {
    	System.Out.println("key + "+s + " value : "+hashMap.get(s));
    }
    
    //key :food1 value : hamburger
    //key :food3 value : ramen
    //key :food2 value : pizza
    //key :food4 value : ramen
    ひずみ発生器の利用
    Mapを反復する際に使用する奇形機を利用することができる.
    Iterator<Entry<String, String>> entries = hashMap.entrySet().iterator();
    		
    while(entries.hasNext()) {
    	Entry<String, String> entry = entries.next();
    	System.out.println(entry.getKey() + entry.getValue());
    }
    上記の例のようにkeySet()でget(key)を使用して反復することができます.
    2つのHashMapを比較
    2つのHashMapを比較する場合は、equals()で比較できます.デフォルトでは、equals()がオブジェクトで使用されている場合、2つのオブジェクトが同じインスタンスを参照しているかどうかがチェックされます.ただし、HashMapでequals()を使用している場合、2つのHashMapが持つエントリが論理的に等しいかどうかのみがチェックされます.前述したように、入る順番は関係ありません.たとえば
    HashMap<Integer, String> map1 = new HashMap<>();
     
    map1.put(1, "A");
    map1.put(2, "B");
    map1.put(3, "C");
     
    //Same as map1
    HashMap<Integer, String> map2 = new HashMap<>();
    
    map2.put(3, "C");
    map2.put(1, "A");
    map2.put(2, "B");
     
    //Different from map1
    HashMap<Integer, String> map3 = new HashMap<>();
    
    map3.put(1, "A");
    map3.put(2, "B");
    map3.put(3, "C");
    map3.put(3, "D");
     
    System.out.println(map1.equals(map2));  //true
    System.out.println(map1.equals(map3));  //false
    前述したようにmap 1とmap 2はいずれもnewキーワードで生成されたオブジェクトであるが,equals()比較ではtrueを返す.
    Key不変性
    HashMapでPOJOをKeyとして使用する場合、equals()とHashCode()は、論理的にピア(オブジェクト上のデータが同じ)比較を行うために過剰に符号化されるべきである.keyはhashCode()の値として格納され、取得時にhashCode()を使用してbucketの位置を検索し、格納されたhashコードと一致するかどうかを判断します.したがって,hashCode()の結果値が同じであってもequals()が別の値と考えられると,それを別のキーとして認識し,望ましくない結果を生じる可能性がある.
    例:
    public class MutableKey{
    	private String name;
    
    	@Override
    	public boolean equals(Object o){
    		// 같은 인스턴스를 참조하고 있으면
    		if(this == o){
    			return true;
    		}
    
    		// 파라미터로 받은 객체가 null을 참조하거나 둘의 인스턴스가 다르면
    		if(o == null || this.getClass() !=o.getClass()){
    			return false;
    		}
    
    		MutableKey that = (MutableKey) o;
    
    		//두 데이터의 값 일치를 비교해서 결과 리턴
    		return Objects.equals(name, that.name);
    	}
    
    	@Override
    	public int hashCode(){
    		// name이 같을 경우 같은 hashcode 리턴
    		return Objects.hash(name);
    	}
    }
    鍵として使用されるPOJOでequals()とhashCode()が上書きされている場合、MutableKeyクラスのnameフィールドを別途変更しなければ、HashMapの鍵として使用され、常に同じhashcodeが返される.
    HashMap<MutableKey, String> hashMap = new HashMap<MutableKey, String>();
    
    hashMap.put(new MutableKey("name"), "John Doe");
    String value = hashMap.get(new MutableKey("name"));
    System.out.println(value); // John Doe
    ただし、name fieldを変更してHashMapから値を取得しようとすると、変更された変数を持つhash codeにアクセスしようとするKeyに等しいため、保存されていない値になるためnullが返されます.
    MutableKey key = new MutableKey("John Doe");
    
    hashMap.put(key, "happy");
    System.out.println(hashMap.get(key)); // happy
    
    key.setName("punch");
    System.out.println(hashMap.get(key)); // null
    したがって,これを予測してコードを記述するか,できるだけ不変の値をキーとして用いることが望ましい.
    この文章はHashMapを理解した.
    references:
    https://www.baeldung.com/java-hashmap-load-factor
    https://www.baeldung.com/java-hashmap
    https://d2.naver.com/helloworld/831311
    https://stackoverflow.com/questions/55333952/is-there-an-array-based-map-implementation
    https://stackoverflow.com/questions/37959941/what-exactly-is-bucket-in-hashmap