HashMap简单实现

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;
            }
        }
    }
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值