HashMap简单实现

来源:互联网 发布:q叔淘宝店叫什么名字 编辑:程序博客网 时间:2024/05/22 05:28

HashMap

HashMap可以看成是一个 `数组+链表/红黑树` 组合成的数据结构。

HashMap,HashTable,ConcurrentHashMap是基于hash表的实现,HashTable和HashMap在代码实现上,基本上是一样的,一个是线程安全的,一个非线程安全。ConcurrentHashMap也是线程安全的,但性能比HashTable好。

HashMap结构模型

重要点

在hashMap中放入(put)元素,有以下重要步骤:

  • 计算key的hash值,获得元素在底层数组中的下标。

  • 通过下标位置定位到底层数组里的元素。

  • 取到第一个元素,判断放入元素的key和当前位置的key是否==或者equals,成立则替换value值,返回旧值。

  • 如果是树,循环树中的节点,判断放入元素的key和当前节点的key是否==或者equals,成立则替换树里的value,并返回旧值,不成立就添加到树里。

  • 第一个元素不匹配就顺着元素的链表结构循环节点,判断放入元素的key是否==或equals节点的key,成立则替换链表里value,并返回旧值,找不到就添加到链表的最后。

判断放入HashMap中的元素要不要替换当前节点的元素,key满足以下两个条件即可替换:

  • 1、hash值相等。

  • 2、==或equals的结果为true。

由于hash算法依赖于对象本身的hashCode方法,所以对于HashMap里的元素来说,hashCode方法和equals方法非常重要,所以equals方法和hashCode方法一般是绑定重写的, 否则会造成hashMap运行结果不正确!

几个常量

    static final float DEFAULT_LOAD_FACTOR = 0.75f; //负载因子    transient int size; //逻辑长度    transient int modCount; //修改次数, 用于fail-fast    int threshold; //阈值,用于扩容,一般等于 (loadFactor * capacity)    //没有空数组初始化会造成空指针异常    static final Node<?,?>[] EMPTY_TABLE = {};    //HashMap的主干数组,就是一个Node数组,初始值为空数组{},主干数组的长度一定是2的次幂。     transient Node<K,V>[] table = (Node<K,V>[]) EMPTY_TABLE;     static final int TREEIFY_THRESHOLD = 8; //链表红黑树转换阈值

数组元素

数组里原对象是Node。

static class Node<K,V> implements Map.Entry<K,V>{    final int hash; //hash值    final K key;    V value;    Node<K,V> next; //指向下一个Node的引用,组成链表结构    Node(int hash, K key, V value, Node<K,V> next) {         //...省略构造函数    }   } 

添加Key-Value对

public V put(K key, V value){    return putVal(hash(key), key, value, false, true);}

计算hash值

static fianl int hash(Object key){    int h;    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);  //高低位优化,降低碰撞几率}

添加到链表

final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {    Node<K,V>[] tab; Node<K,V> p; int n,i;    /**添加元素时table为空,触发resize()**/    if((tab = table)==null || (n = table.length) == 0)         n = (tab.resize()).length;    /** 添加元素时 @if tab[i]位置上没有元素,直接添加      * n为table的逻辑长度,即size      * i = (n - 1) & hash 算法可以保证 i 在 n-1 和 hash 之间      * @else 遍历 通过equals和==判断链表上key和hash是否和目标相同,      * 是则替换,否则添加至链表尾部     **/     //1。从底层数组取值     if((p = tab[i = (n - 1) & hash]) == null)        tab[i] = newNode(hash, key, value, null);  //数组元素转成一个单向链表       else{        Node<K,V> e; K k;        //2。底层数组元素匹配成功,赋值给e        if((hash == p.hash) &&             ((k = p.key) == key || (key != null && key.equals(k)))        e = p;         //3。树结构处理        else if (p instanceof TreeNode)            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);        //4。底层数组第一个没匹配上,遍历查询匹配        else {            for (int binCount = 0; ; ++binCount) {                //添加元素到尾部                if ((e = p.next) == null) {                    p.next = newNode(hash, key, value, null);                    if (binCount >= TREEIFY_THRESHOLD - 1) //链表元素大于阈值                        treeifyBin(tab, hash); //转成红黑树                    break;                }                //同2。                if (e.hash == hash &&                    ((k = p.key) == key || (key != null && key.equals(k)))                    break;                p = e;            }        }        //替换value        if (e != null) {            V oldValue = e.value;            if (!onlyIfAbsent || oldValue == null)                e.value = value;            afterNodeAccess(e);            return oldValue;        }    }    ++modCount;    if (++size > threshold)        resize();    afterNodeInsertion(evict);    return null;}

添加到树

//more code here

扩容

hashMap的扩容由resize方法实现, 这个resize方法只是简单实现, 不是源码.

    private Node<K, V>[] resize() {        int newCap = 0;        //数组为空,初始化一个数组        if (table.length == 0 || table == null){            newCap = DEFAULT_INITIAL_CAPACITY;            Node<K,V>[] newTab = (Node<K,V>[]) new Node[ newCap ];            table = newTab;            return newTab;        }        //数组逻辑长度超过阈值,扩容并复制原数组,数组长度要保证是2的幂次数.        else if (size >= (threshold = (int) (loadFactor * table.length))){            newCap = table.length * 2;                Node<K,V>[] newTab = (Node<K,V>[]) new Node[ newCap ];            transfer(newTab);            table = newTab;            return newTab;        }        //超过最大长度        else if (size > MAXIMUN_CAPACITY){            newCap = HashMap2.MAXIMUN_CAPACITY;            return table;        }        return null;    }    /**     * 把原数组的元素传到扩容后的数组     */    void transfer(Node[] newTable) {        int newCapacity = newTable.length;        for (Node<K,V> e : table) {            while(null != e) {                Node<K,V> next = e.next;                int i = e.hash & (newCapacity - 1);                e.next = newTable[i];                newTable[i] = e;                e = next;            }        }    }
原创粉丝点击