HashSetを深く理解する


文書ディレクトリ
  • HashSetとは?
  • HashSetの継承クラスと実装インタフェースはどれらがありますか?
  • HashSetの主要メンバー変数
  • HashSetのコンストラクション関数
  • 一、HashSet()
  • 二、HashSet(Collection extends E>c)
  • 三、HashSet(int initialCapacity,float loadFactor)
  • 四、HashSet(int initialCapacity)
  • 五、HashSet(int initialCapacity,float loadFactor,boolean dummy)
  • HashSetでよく使われる方法は何ですか?
  • isEmpty()
  • contains(Object o)
  • add(E e)
  • remove(Object o)
  • iterator()
  • keySet()
  • size()
  • clear()
  • まとめ
  • なぜHashSetは重複値を許可しないのですか?
  • HashSetはnull要素として保存できますか?
  • HashSetはいったい秩序があるのか、それとも無秩序なのか.
  • HashSetはスレッドが安全ですか?
  • HashSetから指定された要素を取得するにはどうすればいいですか?

  • ハシュセットって何?
    HashSetはjavaが存在する.utilパッケージのクラスは、Javaでよく使われる集合クラスであり、Setインタフェースの実装クラスであり、このコンテナに重複するオブジェクトを格納できない.HashSetではHashMapに基づいて実装されており、下位層ではHashMapを使用して要素を格納しています.HashMapに関する記事はここを参照してください.
    HashSetの継承クラスと実装のインタフェースはどれらがありますか?
    public class HashSet<E>
        extends AbstractSet<E>
        implements Set<E>, Cloneable, java.io.Serializable {
        
    }
    

    HashSetはAbstractSetクラスを継承し、Set、Cloneable、Serializableインタフェースを実装していることが上記のコードから分かる
    HashSetの主要メンバー変数
    //   ID
    static final long serialVersionUID = -5024744406713321676L;
    
    // HashSet      HashMap   
    private transient HashMap<E,Object> map;
    
    //     ,     value     map  
    private static final Object PRESENT = new Object();
    

    HashSetのコンストラクション関数
    一、HashSet()
    無参のHashSet()
    //            ,         HashMap   
    public HashSet() {
        //    
        map = new HashMap<>();
    }
    

    二、HashSet(Collection extends E>c)
    //                  Set 
    public HashSet(Collection<? extends E> c) {
        //        Map          
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        addAll(c);
    }
    

    三、HashSet(int initialCapacity,float loadFactor)
    HashMapの友达を深く理解したことがあると信じています.ここを見て、知り合いのような感じがしますか?そう、HashMapのinitialCapacityとloadFactorです
    //            
    public HashSet(int initialCapacity, float loadFactor) {
        // initialCapacity(  HashMap      ), loadFactor(  HashMap      )
        map = new HashMap<>(initialCapacity, loadFactor);
    }
    

    四、HashSet(int initialCapacity)
    //            HashSet
    public HashSet(int initialCapacity) {
        // initialCapacity    HashMap      
        map = new HashMap<>(initialCapacity);
    }
    

    五、HashSet(int initialCapacity,float loadFactor,boolean dummy)
    //                    
    // initialCapacity:    
    // loadFactor:    
    // dummy:  
    //  public  ,     LinkedHashSet    
    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        //               LinkedHashMap      
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }
    

    HashSetでよく使われる方法は何ですか?
    isEmpty()
    コレクションに要素が含まれていない場合はtrueを返します.
    public boolean isEmpty() {
        //        HashMap   isEmpty      HashSet     
        return map.isEmpty();
    }
    

    contains(Object o)
    containsは、HashSetに指定された要素が含まれているかどうかを判断し、ある場合はtrueを返します.
    public boolean contains(Object o) {
        //          HashMap  containsKey            key,            true
        return map.containsKey(o);
    }
    

    add(E e)
    既存の要素をHashSetに追加すると、新しく追加した集合要素はHashMapに保存されず、元の要素も変更されません.つまり、HashSetに重複する要素の特性がないことを示します.
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }
    

    add()メソッドのput操作は実際にはHashMapのput操作であり,put操作はputValを返す.
    public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }
    

    hash()とputVal()メソッドの詳細については、ここをクリックしてHashMapを深く理解してください.ここでは説明しません.
    説明:HashSetが入れる値は毎回keyとして扱われ、HashMapのput()メソッドでは一方向チェーンテーブルを遍歴する形で、同じkeyと同じkeyで生成されたhash値に対して、指定した値で古い値を置き換えることができます
    remove(Object o)
    指定した要素がsetに存在する場合、削除
    public boolean remove(Object o) {
        return map.remove(o)==PRESENT;
    }
    

    iterator()
    //    HashSet     ,            
    public Iterator<E> iterator() {
        //      HashMap   keySet        key
        return map.keySet().iterator();
    }
    

    上のコードでは、HashSetの要素が最下位のHashMapのkeyに格納されていることがわかります.keySetが遍歴していることがわかります.HashMapのkeyキーが重複していると、その値が更新され、keyが重複しないことが保証されます.つまり、HashSetが追加した値は重複しません.
    keySet()
    public Set<K> keySet() {
        // AbstractMap    keySet;
        Set<K> ks = keySet;
        
        //   key     
        if (ks == null) {
            //    keySet        
            ks = new KeySet();
            //   
            keySet = ks;
        }
        return ks;
    }
    

    size()
    コレクション内の要素の数を返します.
    public int size() {
        return map.size();
    }
    

    clear()
    HashMapを呼び出すclear()メソッドNodeのすべてのノードをクリア
    public void clear() {
        map.clear();
    }
    

    まとめ
    なぜHashSetは重複値を許さないのですか?
    setは線形構造であり、setの値は繰り返すことができず、hashsetはsetのhash実装である.HashSetの内部はHashMapのkeyを用いて要素を格納しているからである.
    public class HashSetDemo {
        public static void main(String[] args) {
            Set<String> set = new HashSet<>();
            set.add("hello");
            set.add("hello");
            System.out.println("set ------- " + set);
            
            List<String> list = new ArrayList<>();
            list.add("hello");
            list.add("hello");
            System.out.println("list ------ " + list);
        }
    }
    

    出力:
    set ------ [hello] list ------ [hello, hello]
    もう一つ例を挙げる
    public class HashSetDemo {
        public static void main(String[] args) {
            Random random = new Random();
            Set<Integer> set = new HashSet<>();
            
            //    100  20     
            for (int i = 0; i < 100; i++) {
                //    set 
                set.add(random.nextInt(20));
            }
            System.out.println(set);
        }
    }
    

    出力:
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
    出力内容により、20以内の数字100個に重複がないことがわかります
    HashSetはnull要素として保存できますか?
    はい、HashSetの下位層はHashMapによって実現されるので、HashMapはkeyがnullであることを許可するので、HashSetはもちろん
    public class HashSetDemo {
        public static void main(String[] args) {
            Set<String> set = new HashSet<>();
            set.add(null);
            System.out.println(set);
        }
    }
    

    出力:null
    HashSetは秩序あるのか無秩序なのか.
    HashSetは無秩序であり、HashMapのkeyは無秩序であり、HashSetの下位実装はHashMapであるため、HashSetは無秩序である
    秩序と無秩序はどのように理解しますか?
    答え:挿入内容の順序が出力内容の順序と一致している場合は秩序があり、そうでない場合は無秩序である
    上の例をとって検討を続ける
    public class HashSetDemo {
        public static void main(String[] args) {
            Random random = new Random();
            Set<Integer> set = new HashSet<>();
            for (int i = 0; i < 100; i++) {
                set.add(random.nextInt(20));
            }
            System.out.println(set);
        }
    }
    

    出力:
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
    誰?誰が無秩序だと言ったの?これは明らかに秩序があるのではないでしょうか.執着しているあなたたちもきっとおかしいと思います.metoo、OK、検討を続けます.
    もう一つの例を見てみましょう
    public class HashSetDemo {
        public static void main(String[] args) {
            Set<Integer> set = new HashSet<>();
            set.add(1);
            set.add(2);
            set.add(3);
            set.add(4);
            set.add(5);
            for (Integer integer : set) {
                System.out.print(integer + " ");
            }
        }
    }
    

    出力:
    1 2 3 4 5
    まだ秩序があるのか、いったいどうしたのか.
    わざと数字の位置を変えてみる
    public class HashSetDemo {
        public static void main(String[] args) {
            Set<Integer> set = new HashSet<>();
            set.add(1);
            set.add(3);
            set.add(2);
            set.add(4);
            set.add(5);
            for (Integer integer : set) {
                System.out.print(integer + " ");
            }
        }
    }
    

    出力:
    1 2 3 4 5
    OK、今度は無秩序だから、もう一つやってみよう
    public class HashSetDemo {
        public static void main(String[] args) {
            Set<Integer> set = new HashSet<>();
            set.add(5);
            set.add(4);
            set.add(3);
            set.add(2);
            set.add(1);
            for (Integer integer : set) {
                System.out.print(integer + " ");
            }
        }
    }
    
    

    出力:
    1 2 3 4 5
    はい、無秩序に来ました.
    では、最初の例はどういうことですか.以下に説明します.
    説明:作成したHashSetはIntegerタイプであるため、これも最も偶然な点です.IntegerタイプhashCode()の戻り値はint値自体であり、格納時に要素がいくつかの演算によって配列内の位置が得られます.このステップでは、それ自体がダウンスケール(このステップのみを考慮)であるため、実はソート機能が実現されており、intタイプの範囲が広すぎてメモリが置けないため、それを型取り演算し、ハッシュ競合を減らすため、また型取り前に、摂動関数の計算を行い、得られた数を要素ダウンスケールとしてJDK 8でのhashアルゴリズムに従い、これにより、HashMapのhash()演算後もデータは自分自身の値であり、ハッシュ競合は発生しない
    まとめ:だからHashSetはただ秩序を保証しないだけで、無秩序を保証するわけではありません
    HashSetはスレッドが安全ですか?
    いいえ、HashSetはスレッドが安全ではありません.
    HashSetから指定した要素を取得するにはどうすればいいですか?
    SetコレクションをListリストに変換することでgetアクションを使用して指定した要素を取得できます
    public class HashSetDemo {
    
        //               
        public static String getType(Object obj) {
            return obj.getClass().getName().toString();
        }
    
        public static void main(String[] args) {
            Set<Integer> set = new HashSet<>();
            set.add(1);
            set.add(2);
            set.add(3);
            set.add(4);
            set.add(5);
            List<Integer> list = new ArrayList<>(set);
            System.out.println(list.get(1)); //       
            System.out.println(getType(list)); //       
        }
    }
    

    出力:
    2 java.util.ArrayList