HashSetソースコード解析は面接問題から、HashSet内部はどのように実現されていますか?


本明細書のオリジナルアドレス、 :https://jsbintask.cn/2019/03/27/jdk/jdk8-hashset/(食用効果が最も良い)、転載は出典を明記してください!
前言
この間、友达の面接でこの問題に遭遇しました.HashSetの特徴について話します.それはどのように実現されていますか.使用するときに注意しなければならない点は何ですか.ちょうど最近この方面の文章を書いているので、ちょうどこの文章を通じてHashSetのソースコードの実現を説明して、注意しなければならない点です.HashSetSetインターフェースを実現し、重複要素を格納できない容器であり、内部はHashMapを直接使用して実現される.すなわち、下層は配列を使用してデータを格納し、HashSetは同期手段がなく、マルチスレッド環境では慎重に考慮し、Collections.synchronizedSet(new HashSet(...));を使用して従来のSetメソッドに同期することができる.
友情リンク:HashMapソースコードの全解析面接問題から:hashmap put方法を1行1行コードで説明してください
HashSet構造の詳細
クラス構造
関係図に注意すべき点はなく、HashSetSetインターフェースを実現し、Setは の抽象概念であり、その内部に重複する要素が現れることは許されない.
クラスメンバー
先に述べたように、Set内部に重複要素は存在しないが、HashSet内部はどのようにしているのだろうか.図のように、直接HashMapを使用してデータを保存します.HashMapのKeyは唯一でなければならないので、私たちが保存する必要があるデータをKeyに置くことができます.すべてのvalueは1つの内部のオブジェクトPRESENTに対応しています.
ソースの詳細
コンストラクタ
public HashSet(int initialCapacity, float loadFactor) {
    map = new HashMap<>(initialCapacity, loadFactor);
}

簡単明瞭で、HashMapを直接newします.ここで注意しなければならないのは、もう一つの一般的ではない構造方法です.
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
    map = new LinkedHashMap<>(initialCapacity, loadFactor);
}

この構成方法を使用すると、内部ではHashMap操作要素ではなくLinkedHashMapが使用され、LinkedHashMapはHashMapから継承されます.これらの違いは、LinkedHashMapがHashMapの下部で配列を使用しているのは、上に2つの「ポインタ」がそれぞれ頭と尾を指していることです.
add(E e)要素の追加
public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

HashMapのputメソッドを直接呼び出し、要素eをmapのkey位置に配置して一意性を保証します.HashMapのputメソッドは,この位置に同じKey(==またはequals等しい)が既に存在する場合,元の古いvalueを新しいvalueで置き換え,古いvalueを返すことを知っているので,HashSetにとって最初の挿入はnullを返すことに成功し,後で同じ要素を再び挿入し,戻るのはオブジェクトであり,このような要素がすでに存在していることを示している.挿入に失敗しました.他のremoveではcontainsメソッドが似ています.
注意点
  • HashSetに格納されている要素はすべて「無秩序」であり、下位層は配列実装を使用し、格納時にkeyをhashして配列位置を導出するため、ランダムなプロセスであるため、格納されている要素は無秩序である.
  • HashSet反復時のパフォーマンスを考慮すると、初期容量はできるだけ小さく設定できますが、ロードプライマーは大きく設定できます(デフォルト0.75).HashSetとHashMapの注目の中心が異なるため、HashMapはその中のキー値ペアの記憶および拡張時の性能の考慮に注目している.HashSetの反復メソッドは、HashMap内部のKeySetのiteratorメソッドを直接呼び出し、KeyIteratorを返します.
  • public Iterator<E> iterator() {
        return map.keySet().iterator();
    }
    

    この方法は反復時に配列がnullであるか否かをループ判定し,nullでない場合にはその位置に要素があると考える.したがって、初期容量を決定するには、HashSet反復のkeyをできるだけ小さく設定することが有利である.
    まとめ
  • HashSetは、要素が重複せず無秩序な容器である.
  • HashSetの内部ではHashMapを使用して実装され、つまり最終的には配列を使用してデータを格納する.
  • 使用時には、できるだけ容器の大きさを決定し、できるだけ初期容量を小さく設定し、ロードリードを大きくして反復性能を速める必要があります.
  • , !
    関連記事:HashMapソースコードの全解析面接問題から:hashmap putメソッドjdk 1.8ソースコード解析-ARayList jdk 1.8 LinkedListソースコードの全分析スレッドプールを1行1行説明してください.面接?これを見れば十分だ.