Java中HashMap实现原理浅析

来源:互联网 发布:小儿常见病推拿软件 编辑:程序博客网 时间:2024/04/24 08:01

概述

HashMap是基于Map接口的实现。这个实现提供了所有可选的映射操作,并且允许null值和null键。(除了不同步和允许空值以外,HashMap和Hashtable几乎等价)。这个类不保证映射之间的顺序;尤其是,它不保证随着时间的推移映射顺序不变(翻译自Java API文档)。


哈希表是一个“链表散列”的结构,是由数组和链表结合而成。

HashMap的基层是一个数组结构,而数组中的每一项又可以拓展成为一个链表。具体的实现形式Entry就是数组中的元素,它是由key和value组成的基本单元,其本身持有Entry的引用来构成链表形式。


基本方法实现

此处源码版本为JDK1.7。

put方法

    public V put(K key, V value) {
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key);
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }

其实现的过程如下:

首先检测key值,并由此得出它在数组中的index值,判断是否已有key存在,若已在则覆盖;若不存在则将新元素加到链表后,加元素操作具体由addEntry方法实现。


在addEntry方法中包含有扩容判断和createEntry方法,createEntry方法将新创建的 Entry 放入 bucketIndex 索引处,并让这个新的 Entry 指向原来的 Entry。


get方法

    public V get(Object key) {
        if (key == null)
            return getForNullKey();
        Entry<K,V> entry = getEntry(key);

        return null == entry ? null : entry.getValue();
    }


    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;
    }

get方法中用getEntry来判断key值在原数组中是否已存在并做取值操作。


总体来说,HashMap的底层是用Entry数组来实现的,通过key的哈希函数来寻找指定的key-value对位置并进行操作。


HashMap的refresh操作

当HashMap中的元素个数超过数组大小乘以负载因子时,就需要对HashMap进行扩容,类似的操作在List中也有。

    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);
    }


新建Entry数组后将原数组中的元素转移到新数组中,即重新计算它们在新数组中的位置。默认的负载因子是0.75,扩容规模为原来的2倍。


以上为对HashMap实现的基本理解,更详细的内容参考http://zhangshixi.iteye.com/blog/672697


0 0