• HashMap的原理,我懂了。(未完全版本)


    HashMap的数据结构

    • 数组+链表+红黑树
    • 数据是以节点的方式来存储的,每个节点中包含:Key,Value,Next指针,
      链表是为了规避哈希冲突而存在的,因为哈希冲突理论上是不能避免的。
      红黑树是为了解决链表长度存在多的时候,解决效率问题而存在的, 节点大于8,

    默认大小是16,负载因子是0.75

    面试题

    所有的问题都是对知识点的理解,如果有不懂的问题,需要反向反思自己是否理解这个知识点。

    1. put同一个key的时候,返回值是之前的值。(源码中是这样写的。)
    2. HashMap的数据结构是怎么样的?为什么会使用链表结构,为什么会使用数组结构,为什么1.8开始会使用红黑树
      a. 数组1.7存的就是Key-Value的Entry,1.8寸的时候Node
    3. 链表新增数据的时候,是放在头结点还是尾结点。?
      a. 1.7采用的链表的头插法 (多线程扩容的时候会导致循环卡死。)
      b. 1.8采用链表的尾插法
    4. get的逻辑和问题,get的时候先经过hash计算只能取到数组的位置。(先取到头结点)
    5. 为什么HashMap在初始化数组容量的时候,一定要是2的次方数呢?
    6. 如何解决Hash冲突导致的链表很长的问题?会导致get方法很慢!
    7. HashMap的数组为什么需要扩容?==》为了提高get的效率。
    8. HashMap扩容的步骤是什么呢?
    9. HashMap扩容会导致哪些问题呢? 链表翻转
    10. HashMap不是线程安全的,多线程扩容可能会让链表循环搬运。搬运的时候,对导致两个节点形成循环链表。
      a. 多线程扩容的时候,会导致这个问题
      b. 问题原因:链表插入使用的是头插法,头插法的特性。两个线程共同调用扩容的时候就会出现循环。所以1.8版本就把它改成了尾插法。
    11. HashMap什么时候会进行rehash()
    12. HashMap的key为null的时候,是存在index为0的位置上的。

    相关代码摘要

    /**
     * Returns a power of two size for the given target capacity.
     */
    static final int tableSizeFor(int cap) {
        int n = cap - 1;
        n |= n >>> 1;
        n |= n >>> 2;
        n |= n >>> 4;
        n |= n >>> 8;
        n |= n >>> 16;
        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
    }
    这个方法太牛了。获取最近位置的2的次方数。
    只能感叹JDK作者对于位运算的使用已经达到了出神入化的境界了。
    
    问题:为什么HashMap在初始化数组容量的时候,一定要是2的次方数呢?
    
    newTab[e.hash&(newCap-1)]=e;
    需要进行或操作,来计算数组下标位置方法的时候,这个算法的应用就倒着推出来的newCap必须要是2的次方。
    
    问题:如何解决Hash冲突导致的链表很长的问题?会导致get方法很慢!
    H ^= k.hashcode()
    这个方法为了提高最终hashcode的散列性
    
    

    参考链接:

    1. 安其拉的博客:一个HashMap跟面试官扯了半个小时
    2. 跟着Mic学架构:HashMap的原理
    3. 图灵-周瑜,视频讲解1.7的hashmap原理:推荐
  • 相关阅读:
    六、开闭原则
    五、迪米特法则
    绘制禁用图像
    程序自启动的问题
    金山也开始做“QQ”了
    TextBox只能输入数字的两种解决办法
    Chrome的一点小问题
    OOAD读书笔记(三):需求变化
    OOAD读书笔记(六):如何解决大问题
    J道,学习分析设计的Java社区
  • 原文地址:https://www.cnblogs.com/dawabigbaby/p/16067473.html
Copyright © 2020-2023  润新知