面试题 -- hashmap

参考:https://www.jianshu.com/p/9ca74bdfdb6b

1.HashMap底层实现原理

2.HashMap工作原理

3.HashMap冲突产生及解决

4.HashMap不同步原因分析及解决办法

5.HashMap扩容

首先,我学习时JDK版本为1.8(在网上搜的,因为我学的时候已经是1.8了,自行百度了解,有错误指出谢谢)

JDK1.8中对Hashmap做了以下改动。

  • 引入红黑树,优化数据结构
  • 使用一个Node数组来存储数据,但这个Node可能是链表结构,也可能是红黑树结构。
  • 将链表头插法改为尾插法
  • 优化hash算法
  • 出现哈希冲突时,1.7把数据存放在链表,1.8是先放在链表,链表长度超过8就转成红黑树

源码

public class HashMap<K,V> extends AbstractMap<K,V>implements Map<K,V>, Cloneable, Serializable {}

基本属性

private static final long serialVersionUID = 362498820763181265L; // 序列号
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // (默认数组长度)初始容量为16
static final int MAXIMUM_CAPACITY = 1 << 30; // 最大数组容量2^30
static final float DEFAULT_LOAD_FACTOR = 0.75f; // 在构造函数中未指定时使用的负载因子。默认负载因子0.75
static final int TREEIFY_THRESHOLD = 8; // (链表转红黑树的阈值) 桶的树化阈值:即 链表转成红黑树的阈值,在存储数据时,当链表长度 > 该值时,则将链表转换成红黑树
static final int UNTREEIFY_THRESHOLD = 6; // (扩容时红黑树转链表的阈值)桶的链表还原阈值:即 红黑树转为链表的阈值,当在扩容(resize())时(此时HashMap的数据存储位置会重新计算),在重新计算存储位置后,当原有的红黑树内数量 < 6时,则将 红黑树转换成链表
static final int MIN_TREEIFY_CAPACITY = 64; //  最小树形化容量阈值:即 当哈希表中的容量 > 该值时,才允许树形化链表 (即 将链表 转换成红黑树)
// 否则,若桶内元素太多时,则直接扩容,而不是树形化
// 为了避免进行扩容、树形化选择的冲突,这个值不能小于 4 * TREEIFY_THRESHOLD

transient Node<K,V>[] table; // 存储元素的数组,允许长度为0,总是2的幂次倍
transient Set<Map.Entry<K,V>> entrySet; // key-value集合
transient int size; // 映射中包含的键-值映射的数目(存放元素的数组,但是不等于数组的长度!!!)
transient int modCount; // 每次扩容或更改map结构的计数器
int threshold; // 临界值,当实际大小(容量*装载系数)超过临界值时,会进行扩容。

构造函数

1.初始容量(未定义)负载因子(未定义)
public HashMap(int initialCapacity, float loadFactor) {
		// 初始容量不能小于0,否则会报错
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        // 初始容量大于最大值,只能为最大值,不能再扩容了
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        // 负载因子不能小于0或者不为数字,抛出异常
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);
        this.loadFactor = loadFactor; // 初始化负载因子
        this.threshold = tableSizeFor(initialCapacity); // 初始化临界值
    }
2.初始容量(未定义)负载因子(0.75默认)
public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }
3.初始容量(16默认)负载因子(0.75默认)
public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR; // 所有其他字段为默认值
}

HashMap是数组+链表/红黑树(JDK1.8增加了红黑树部分)实现的

HashMap是基于hash算法实现的,通过put(key,value)存储,get(key)获取。当传入key时,HashMap会根据key.hashcode()计算出hash值,根据hash值将value保存在bucket里。当计算出的hash值相同时,我们称之为hash冲突,HashMap的做法是用链表或红黑树存储相同hash值的value。

例如:同一hash值的元素已经被放在数组同一位置了,这时就添加到同一hash值的元素的后面,他们在数组的同一位置,但是形成了链表,同一各链表上的Hash值是相同的,所以说数组存放的是链表。而当链表长度太长时,链表就转换为红黑树,提高查找的效率。

bucket桶:桶是每个所指数组的元素。在较早的Java版本中,每个存储桶都包含一个Map条目的链接列表。在新的Java版本(1.8版本)中,每个存储桶都包含条目的树结构或条目链接列表。

工作原理

1.put(key,value)存储对象

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

HashMap并没有直接提供putVal接口给用户调用,而是提供的put函数,而put函数就是通过putVal来插入元素的。

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未初始化或者长度为0,进行扩容
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length; // 扩容
    // 情况A:桶为空,直接将元素放入当前位置
    // 根据 hash 值确定节点在数组中的插入位置,若此位置没有元素则进行插入,注意确定插入位置所用的计算
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
回答: HashMap是Java中的一个常用数据结构,它的底层是由hash数组和单向链表实现的。每个数组元素都是一个链表,通过Node内部类实现了Map.Entry接口来存储键值对。HashMap通过put和get方法来存储和获取数据。\[1\] 在重写equals方法时,我们需要同时重写hashCode方法。这是因为在HashMap中,查找value是通过key的hashCode来进行的。当找到对应的hashCode后,使用equals方法来比较传入的对象和HashMap中的key对象是否相同。因此,为了保证正确的查找和比较,我们需要同时重写equals和hashCode方法。\[2\]\[3\] HashMap在什么时候进行扩容呢?当HashMap中的元素数量超过了负载因子(默认为0.75)与当前容量的乘积时,就进行扩容。扩容是为了保持HashMap的性能,因为当元素数量过多时,链表的长度变长,查找效率下降。扩容的过程是创建一个新的数组,将原数组中的元素重新分配到新数组中,然后将新数组替换为原数组。\[3\] #### 引用[.reference_title] - *1* *2* *3* [史上最全Hashmap面试总结,51道附带答案,持续更新中...](https://blog.csdn.net/androidstarjack/article/details/124507171)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值