HashMap相关
HashMap在1.7和1.8的差别
1.7数组加链表,存储节点Entry,使用头插法
1.8是数组加链表加红黑树,存储节点为Node,使用尾插法,链表大小超过8个时会自动转换为红黑树
put方式和hash计算方式
put的时候会根据Key的hash值去计算一个index的值,但是hash存在概率性,哈希冲突,不同的key会存在相同的hash值,那么hash值相同的数据就回城Entry节点延申形成链表,每个节点都会保存自身的hash,key,value。
为什么从头部插入改为尾部插入了呢
与HashMap的扩容机制有关,当当前的长度超过扩容的阈值(初始长度x0.75),接下来存入的数据就会进行resize(是在put的时候进行判断),创建一个新的数组,长度为原来的两倍,调用了一个transfer方法,然后对entry里的数据进行rehash,遍历数组,把里面的所有节点重新计算hash再放入数组中(因为长度发生变化之后index计算规则也发生了改变)
HashMap的默认初始化长度是多少?为什么是16
16是2的幂进行位运算很方便,可以实现hashcode的均匀分布。
为什么重写equals和hashCode方法
存(计算hash值,hash值不同放置数组,无需equals,hash值相同,比较内容,不同则放入,相同则不放入.)
取(先比较hash值,不同直接取values 相同equals确定具体key,取values)
因为放入hashmap的时候会根据hash值来分配键值对的位置,但是hash有概率性,不同的键会存在相同的hash值,这个时候需要比较它们的内容是否相同,在object中 equals方法内部是用的==比较,==比较基本数据类型是比较值,比较引用数据类型是比较地址值,这里我们需要比较内容,所以需要重写equals方法
HashMap源码参数
默认的初始化容量
最大的容量
负载因子
扩容阈值
转红黑树的阈值
最小红黑树容量
HashMap不安全,那么我们平时怎么处理hashmap的线程安全问题呢?
HashTable,Collections.synchronizedMap()或者ConcurrentHashMap 但是前两者的并发度很低,基本都是用ConcurrentHashMap.
HashTable如何实现线程安全?
hashtable和hashmap相比是线程安全的,适合多线程的情况下使用,但是并发度很低,效率低hashtable对数据进行操作的适合都会上锁,直接在方法上上锁.
ConcurrentHashMap如何实现线程安全?
value使用volatile修饰,保证了该value值的内存可见性,禁止指令重排序
HashMap 和HashTable区别
方法修饰:Hashtable 中的方法是Synchronized的,而HashMap中的方法在缺省情况下是非Synchronize的。
父类:二者继承的类 不同 HashTable的父类是Dictionary HashMap的父类AstractMap
包含方法:二者是否有contains方法 HashTable有 HashMap无
初始容量和扩容大小不同: table=11 map=16,扩容 table 为原来的容量*2+1 map为原来的容量*2
二者的负载因子都是0.75
key value值:table key和value都不可以为null,put 空值的时候会直接抛空指针异常map可以,但是运行时会抛出NullPointerException异常
Hashtable使用的是安全失败机制(fail-safe),这种机制会使你此次读到的数据不一定是最新的数据。如果你使用null值,就会使得其无法判断对应的key是不存在还是为空,因为你无法再调用一次contain(key)来对key是否存在进行判断,ConcurrentHashMap同理。
快速失败机制
快速失败(fail—fast)是java集合中的一种机制, 在用迭代器或者增强for循环遍历一个集合对象时,如果遍历过程中对集合对象的内容进行了增加或者删除,则会抛出Concurrent Modification Exception迭代器/增强for循环在遍历时直接访问集合中的内容,并且在遍历过程中使用一个modCount 变量。集合在被遍历期间如果元素数量发生变化,就会改变modCount的值。
每当迭代器使用hashNext()/next()遍历下一个元素之前,都会检测modCount变量是否为expectedmodCount值,是的话就返回遍历;否则抛出异常,终止遍历。
HashSet底层实现原理
HashSet 是基于 HashMap 实现的,HashSet 底层使用 HashMap 的key来保存所有元素(value是一个object对象),因此 HashSet 的实现比较简单,相关 HashSet 的操作,基本上都是直接调用底层 HashMap 的相关方法来完成,HashSet 不允许重复的值。
HashMap的底层原理
在JDK1.7及之前,是用数组加链表的方式存储的。在JDK1.8之后,是使用数组加链表加红黑树实现的
红黑树
每个节点只有两种颜色,红色或者黑色
根节点必须是黑色
每个叶子节点(NIL)都是黑色的空节点
从根节点到子节点,不能出现两个连续的红色节点
从任意节点出发,到下边的子节点的路径包含的黑色节点数目都相同
常用的变量(JDK1.8)
默认的初始化容量为16,必须是2的n次幂
DEFAULT_INITIAL_CAPACITY = 1 << 4;
最大的容量为2的30次幂
MAXIMUM_CAPACITY = 1 << 30
默认的加载因子为0.75,乘以数组容量得到的值用来表示当元素个数达到多少时需要扩容
DEFAULT_LOAD_FACTOR = 0.75f;
为什么要设置0.75这个值呢?
若小于0.75如0.5则数组长度达到一半大小就会需要扩容,空间使用率会大大的降低
若大于0.75,如0.8则会正大hash冲突的概率影响查询效率
简而言之就是时间和红箭的权衡
当链表长度过长时有一个阈值,超过8这个阈值就会讲链表转换为红黑树
TREEIFY_THRESHOLD = 8;
当链表长度过短(达到6这个阈值)的时候,红黑树就会退化为链表
UNTREEIFY_THRESHOLD = 6;
链表除了有阈值的限制还有另外一个限制,当数组容量至少达到64才会树化,为了避免数组扩容和树化之间的冲突
MIN_TREEIFY_CAPACITY = 64;
每次结构改变时都会自增,fail-fast机制,这是一种错误检测机制,当迭代集合时,如果结构发生改变,则会发生fail-fast,抛出异常
MIN_TREEIFY_CAPACITY = 64;
HashMap的构造函数
默认无参构造,指定一个默认的加载因子
可指定容量的有参构造(使用默认的加载因子)(如果指定的容量不为2的最小次幂值,则会自动转换,如传过来的容量为14,则返回16)
可指定容量加载因子,但是不建议自己手动指定非0.75的加载因子
可传入一个已有的map
HashMap的数据结构?
哈希表(数组加链表),当链表长度超过8时转换为红黑树
HashMap线程不安全,那么线程不安全会导致什么问题?
数据竞争(Data Race)
问题描述:当多个线程同时访问和修改共享数据时,可能会出现数据竞争。如果线程的执行顺序不同,最终结果可能会不同。
示例:多个线程同时对一个共享变量进行递增操作,最终的结果可能小于预期。
影响:数据不一致、程序行为不可预测。
数据损坏(Data Corruption)
问题描述:由于线程不安全,可能导致共享数据被错误地修改,从而损坏数据。
示例:多个线程同时写入同一个文件,可能导致文件内容混乱。
影响:数据丢失、数据错误、程序崩溃。
死锁(Deadlock)
问题描述:当多个线程互相等待对方持有的资源时,会导致死锁。线程无法继续执行,程序陷入僵持状态。
示例:线程A持有资源1并请求资源2,线程B持有资源2并请求资源1,两个线程都无法继续执行。
资源泄漏(Resource Leak)
问题描述:由于线程不安全,可能导致资源(如文件句柄、数据库连接、内存等)未被正确释放。
示例:多个线程同时打开文件句柄,但只有一个线程负责关闭,可能导致文件句柄未被关闭。
影响:资源耗尽、系统性能下降、程序崩溃。
性能问题
问题描述:线程不安全可能导致线程之间的频繁冲突和同步开销,降低程序性能。
示例:多个线程频繁地对共享数据进行加锁和解锁操作,导致性能下降。
影响:程序运行缓慢、响应时间增加。
不可重复性(Non-deterministic Behavior)
问题描述:线程不安全可能导致程序的行为依赖于线程的调度顺序,每次运行结果可能不同。
示例:多个线程同时修改一个共享变量,程序的输出结果每次运行可能不同。
影响:难以调试和测试,程序的可维护性差。
异常行为(Unexpected Behavior)
问题描述:线程不安全可能导致程序出现意料之外的行为,如程序崩溃、数据错误等。
示例:多个线程同时访问一个未初始化的共享变量,可能导致程序崩溃。
影响:程序运行不稳定,用户体验差。
HashMap的工作原理?
HashMap基于hashing原理,通过put()和get()方法储存和获取对象。当将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算hashcode,让后找到bucket位置来储存值对象。当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用链表来解决碰撞问题,当发生碰撞了,对象将会储存在链表的下一个节点中。 HashMap在每个链表节点中储存键值对对象。
posted on 2025-12-11 16:22 lubingliang 阅读(4) 评论(0) 收藏 举报
浙公网安备 33010602011771号