HashMap和HashSet的底层实现

来源:互联网 发布:网络加速器国外节点 编辑:程序博客网 时间:2024/06/04 20:03

HashMap和HashSet是Map接口、Set接口常用的实现类,因此研究这两个实现原理有助于更好的使用它们,并理解他们的区别。

下面先来分析HashMap的底层实现。

Map中存储的是<Key,value>对,每个<Key,value>对都以Map.Entry的数据结构存储,然后用一个table数组来存储Entry,该table就是根据实例化的参数创建的数组。

存储的位置则按照Key计算的hash返回值来计算。

以下是Java源代码HashMap类中添加元素的函数。

    void addEntry(int hash, K key, V value, int bucketIndex) {
        if ((size >= threshold) && (null != table[bucketIndex])) {
            resize(2 * table.length);
            hash = (null != key) ? hash(key) : 0;
            bucketIndex = indexFor(hash, table.length);
        }

        createEntry(hash, key, value, bucketIndex);
    }

    void createEntry(int hash, K key, V value, int bucketIndex) {
        Entry<K,V> e = table[bucketIndex];
        table[bucketIndex] = new Entry<>(hash, key, value, e);
        size++;
    }

   void resize(int newCapacity) {
        Entry[] oldTable = table;
        int oldCapacity = oldTable.length;
        if (oldCapacity == MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return;
        }


        Entry[] newTable = new Entry[newCapacity];
        transfer(newTable, initHashSeedAsNeeded(newCapacity));
        table = newTable;
        threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
    }

由此可以看出,当数组的元素已满时,则double其容量。

然后在bucketindex位置创建一个新的Entry,如果此处已经有一个Entry,则新的Entry指向原来的Entry,产生一个Entry链,并且新放入的Entry永远处于Entry链的起端,位于数组bucket中。

当查询元素时,调用get()函数。

    final Entry<K,V> getEntry(Object key) {
        if (size == 0) {
            return null;
        }


        int hash = (key == null) ? 0 : hash(key);
        for (Entry<K,V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            if (e.hash == hash &&
                ((k = e.key) == key || (key != null && key.equals(k))))
                return e;
        }
        return null;
    }

可见新根据key计算的hash值去找相应的bucket,如果该bucket只有一个元素则直接返回该Value,如果存在一个Entry链的话,必须遍历整个Entry链才能找到相应的value值。

由此可见,当table中的每个bucket中都只有一个Entry元素时,该hashMap的性能最好。


明白了HashMap的原理,HashSet就迎刃而解了。因为HashSet的底层就是用HasHMap来实现的,即把HashMap中的key来存储元素即实现了set.

我们来看一下HashSet的源码便知。


    public HashSet() {
        map = new HashMap<>();
    }
    public HashSet(Collection<? extends E> c) {
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        addAll(c);
    }
    public HashSet(int initialCapacity, float loadFactor) {
        map = new HashMap<>(initialCapacity, loadFactor);
    }
    public HashSet(int initialCapacity) {
        map = new HashMap<>(initialCapacity);
    }
    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }

 




0 0
原创粉丝点击