hashmap底层原理个人话术总结

jdk1.7和1.8有什么区别

1.7之前:数组+链表

1.8之后:数组+链表+红黑树

数组:对于查询效率较高,但是对于删除增加效率低

链表:对于删除增加效率高,但是对于查询效率低

计算Hash值: HashTable直接使用对象的hashCode。 HashMap的Hash值:(key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);

三目运算 式子合理取?不合理取:

位运算符 ^ 相同0 不同1

HashMap和Hashtable的区别

继承: Hashtable继承自Dictionary类,而HashMap继承自AbstractMap类。但二者都实现了Map接口。

锁: Hashtable 中的方法是Synchronize的,而HashMap中的方法在缺省情况下是非Synchronize的。

方法: HashMap把Hashtable的contains方法去掉了,改成containsValue和containsKey,因为contains方法容易让人引起误解。 Hashtable则保留了contains,containsValue和containsKey三个方法,其中contains和containsValue功能相同。

是否可以为null: Hashtable中,key和value都不允许出现null值。 HashMap中,null可以作为键,这样的键只有一个。 Hashtable中有类似put(null,null)的操作,编译同样可以通过,因为key和value都是Object类型,但运行时会抛出NullPointerException异常,这是JDK的规范规定的。 Tips: 当get()方法返回null值时,可能是 HashMap中没有该键,也可能使该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键, 而应该用containsKey()方法来判断。

遍历: Hashtable、HashMap都使用了 Iterator。但是,Hashtable还使用了Enumeration的方式 。 容量: HashTable在不指定容量的情况下的默认容量为11,而HashMap为16, Hashtable不要求底层数组的容量一定要为2的整数次幂,而HashMap则要求一定为2的整数次幂。 Hashtable扩容时,将容量变为原来的2倍加1,而HashMap扩容时,将容量变为原来的2倍。

负载因子为什么是0.75.为什么到8的时候转红黑树

如果你看过源代码,你会发现在初始条件下,HashMap在时间和空间两者间折中选择了0.75。

/**
* The load factor used when none specified in constructor.
*/
​
static final float DEFAULT_LOAD_FACTOR = 0.75f;

但是为什么一定是0.75?而不是0.8,0.6,这里有一个非常重要的概念:泊松分布。

相信大家都学过概率论,对这个大名鼎鼎的定律感觉应该是既熟悉又陌生。本篇文章的重点不是为大家普及概率论知识,这里就简单介绍下。

泊松分布是最重要的离散分布之一,它多出现在当X表示在一定的时间或空间内出现的事件个数这种场合。

举个简单的例子,假如你一个老板,新开张了一家酒店,这个时候应该如何准备一天所用的食材呢?

准备的太多,最后卖不掉这么多菜只能浪费扔掉;准备不够,又接不了生意。但是你有很多同行和朋友,他们会告诉你很多经验。

比如把一天分成几个时间段,上午、下午、晚上每个时间段大概会来多少个客人,每一桌大概会点几个菜。综合下来,就可以大致知道在一天的时间内,估计出需要准备的食材数量。

翻译过来说的是,在理想情况下,使用随机哈希码,节点出现的频率在 hash 桶中遵循泊松分布。

对照桶中元素个数和概率的表,可以看到当用 0.75 作为加载因子时,桶中元素到达 8 个的时候,概率已经变得非常小,因此每个碰撞位置的链表长度超过 8 个是几乎不可能的,因此在链表节点到达 8 时才开始转化为红黑树。

hash code和eques之间有什么关系

1、equals方法用于比较对象的内容是否相等(覆盖以后)

2、hashcode方法只有在集合中用到

3、当覆盖了equals方法时,比较对象是否相等将通过覆盖后的equals方法进行比较(判断对象的内容是否相等)。

4、将对象放入到集合中时,首先判断要放入对象的hashcode值与集合中的任意一个元素的hashcode值是否相等,如果不相等直接将该对象放入集合中。如果hashcode值相等,然后再通过equals方法判断要放入对象与集合中的任意一个对象是否相等,如果equals判断不相等,直接将该元素放入到集合中,否则不放入。

5、将元素放入集合的流程图: 在这里插入图片描述

concurrenthash map底层原理

1. ConcurrentHashMap的整体架构

1

如图,这是ConcurrentHashMap在jdk1.8中的存储结构,它是由数组 ,单项链表,红黑树来构成,当我们去初始化一个ConcurrentHashMap实例的时候,默认会初始化一个长度等于16的数组,由于ConcurrentHashMap它的核心仍然是Hash表,所以必然会存在Hash冲突的问题,所以ConcurrentHashMap采用链式寻址的方式,来解决Hash表的冲突,当Hash冲突比较多的时候,会造成链表长度较长的问题,这种会使得ConcurrentHashMap中的一个数组元素的查询复杂度会增加,所以在jdk1.8里面,引入了红黑树的机制,当数组长度大于64并且链表长度大于等于8的时候,单向链表会转化成红黑树,另外随着ConcurrentHashMap的一个动态扩容,一旦链表的长度小于8,红黑树会退化成单向链表

2. ConcurrentHashMap的基本功能

2

ConcurrentHashMap本质上是一个HashMap,因此功能和HashMap是一样的,但是ConcurrentHashMap在HashMap的基础上提供了并发安全的一个实现。并发安全的主要实现主要通过对于Node节点去加锁,来保证数据更新的安全性

3. ConcurrentHashMap在性能方面的优化

如何在并发性能和数据安全性之间去做好平衡,在很多地方都有类似的设计,比如说cpu的三级缓存,mysql的buffer_poor,Synchronized的一个锁升级等等,ConcurrentHashMap也做了类似一个优化,主要体现在几个方面

  1. 在jdk1.8里面ConcurrentHashMap锁的粒度,是数组中的某一个节点,而在jdk1.7里面。它锁定的是Segment,锁的范围要更大,所以性能上它会更低。

  2. 引入红黑树这样一个机制,去降低了数据查询的时间复杂度,红黑树的时间复杂度实是O(logn) 3 如图,当数组长度不够的时候,ConcurrentHashMap它需要对数组进行扩容,而在扩容时间上,ConcurrentHashMap引入了多线程并发扩容的一个实现,简单来说多个线程对原始数组进行分片,分片之后,每个线程去负责一个分片的数据迁移,从而去整体的提升了扩容过程中的数据迁移的一个效率

  3. ConcurrentHashMap它有一个size()方法来获取总的元素个数,而在多线程并发场景中,在保证原子性的前提下去实现元素个数的累加,性能是非常低的,所以ConcurrentHashMap这个方面做了两个优化

  • (1)4

如图,当线程竞争不激烈的时候,直接采用CAS的方式,来实现元素个数的一个递增

  • (2) 如果线程竞争化比较激烈的情况下,使用一个数组来维护元素个数,如果要增加总的元素个数的时候,直接从数组中随机选择一个,在通过CAS算法来实现原子递增,它的核心思想是引入数组来实现并发更新的一个负载

重载和重写有什么区别

一、定义上的区别:

1、重载是指不同的函数使用相同的函数名,但是函数的参数个数或类型不同。调用的时候根据函数的参数来区别不同的函数。 2、覆盖(也叫重写)是指在派生类中重新对基类中的虚函数(注意是虚函数)重新实现。即函数名和参数都一样,只是函数的实现体不一样。

二、规则上的不同:

1、重载的规则: ①必须具有不同的参数列表。 ②可以有不同的访问修饰符。 ③可以抛出不同的异常。

2、重写方法的规则: ①参数列表必须完全与被重写的方法相同,否则不能称其为重写而是重载。 ②返回的类型必须一直与被重写的方法的返回类型相同,否则不能称其为重写而是重载。 ③访问修饰符的限制一定要大于被重写方法的访问修饰符。 ④重写方法一定不能抛出新的检查异常或者比被重写方法申明更加宽泛的检查型异常。

三、类的关系上的区别:

重写是子类和父类之间的关系,是垂直关系;重载是同一个类中方法之间的关系,是水平关系

string,stringbuffer, string budder有什么区别

String是不可变的,由String创建的对象需要更改的时候要重新在堆内存中进行分配,所以说消耗资源大,String是由final修饰的,不能被重写。

StringBuilder和StringBuffer是可变的

StringBuilder和StringBuffer的区别:StringBuilder的效率高,所以说是线程不安全的,而StringBuffer的效率低,因此是安全的,在一般情况下我们会去使用StringBuilder因为效率较高,但是如果要求要线程的话要使用StringBuffer。StringBuffer底层是通过锁来保证安全的。

jdk1.8做了哪些改进

一、改进

1,jdk1.7底层采用entry数组+链表的数据结构,而1.8采用node数组+链表/红黑树的数据结构。

2,jdk1.7的HashMap插入新值时使用头插法,1.8使用尾插法。

使用头插法比较快,但在多线程扩容时会引起倒序和闭环的问题。所以1.8就采用了尾插法。

3,扩容后新表中的索引位置计算方式不同,jdk1.7扩容时是将旧表元素的所有数据重新进行哈希计算,即hashCode & (length-1)。而1.8中扩容时只需将hashCode和老数组长度做与运算判断是0还是1,是0的话索引不变,是1的话索引变为老索引位置+老数组长度。

Java8中HashMap为什么要用红黑树?

在jdk1.8版本后,Java对HashMap做了改进,在链表长度大于8的时候,将后面的数据存在红黑树中,以加快检索速度。

java8中HashMap数据结构 在这里插入图片描述

为什么选择红黑树?

红黑树相比avl树,在检索的时候效率其实差不多,都是通过平衡来二分查找。但对于插入删除等操作效率提高很多。红黑树不像avl树一样追求绝对的平衡,他允许局部很少的不完全平衡,这样对于效率影响不大,但省去了很多没有必要的调平衡操作,avl树调平衡有时候代价较大,所以效率不如红黑树。`

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不凡~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值