摘要:对,分别对和进行排序。主要保存对象的信息,主要是方法。组件主要是通过对进行缓存。同步控制中是一个静变量,那么随之而来的就是的同步问题。现在的问题在于如果获取不了对象时会要执行设置操作操作,此时并发问题随之而来。
一.hashmap的底层原理。
1.hashmap的数据结构
Hashmap实际上是一个数组和链表的结合体(在数据结构中,一般称之为“链表散列“),请看下图(横排表示数组,纵排表示数组元素【实际上是一个链表】)。
从图中我们可以看到一个hashmap就是一个数组结构,当新建一个hashmap的时候,就会初始化一个数组。
上面的Entry就是数组中的元素,它持有一个指向下一个元素的引用,这就构成了链表。
当我们往hashmap中put元素的时候,先根据key的hash值得到这个元素在数组中的位置(即下标),然后就可以把这个元素放到对应的位置中了。如果这个元素所在的位子上已经存放有其他元素了,那么在同一个位子上的元素将以链表的形式存放,新加入的放在链头,最先加入的放在链尾。从hashmap中get元素时,首先计算key的hashcode,找到数组中对应位置的某一元素,然后通过key的equals方法在对应位置的链表中找到需要的元素。从这里我们可以想象得到,如果每个位置上的链表只有一个元素,那么hashmap的get效率将是最高的.
2.hash算法
我们可以看到在hashmap中要找到某个元素,需要根据key的hash值来求得对应数组中的位置。如何计算这个位置就是hash算法。前面说过hashmap的数据结构是数组和链表的结合,所以我们当然希望这个hashmap里面的元素位置尽量的分布均匀些,尽量使得每个位置上的元素数量只有一个,那么当我们用hash算法求得这个位置的时候,马上就可以知道对应位置的元素就是我们要的,而不用再去遍历链表。
首先算得key得hashcode值,然后跟数组的长度-1做一次“与”运算(&)。看上去很简单,其实比较有玄机。比如数组的长度是2的4次方,那么hashcode就会和2的4次方-1做“与”运算。很多人都有这个疑问,为什么hashmap的数组初始化大小都是2的次方大小时,hashmap的效率最高,我以2的4次方举例,来解释一下为什么数组大小为2的幂时hashmap访问的性能最高。
看下图,左边两组是数组长度为16(2的4次方),右边两组是数组长度为15。两组的hashcode均为8和9,但是很明显,当它们和1110“与”的时候,产生了相同的结果,也就是说它们会定位到数组中的同一个位置上去,这就产生了碰撞,8和9会被放到同一个链表上,那么查询的时候就需要遍历这个链表,得到8或者9,这样就降低了查询的效率。同时,我们也可以发现,当数组长度为15的时候,hashcode的值会与14(1110)进行“与”,那么最后一位永远是0,而0001,0011,0101,1001,1011,0111,1101这几个位置永远都不能存放元素了,空间浪费相当大,更糟的是这种情况中,数组可以使用的位置比数组长度小了很多,这意味着进一步增加了碰撞的几率,减慢了查询的效率!
所以说,当数组长度为2的n次幂的时候,不同的key算得得index相同的几率较小,那么数据在数组上分布就比较均匀,也就是说碰撞的几率小,相对的,查询的时候就不用遍历某个位置上的链表,这样查询效率也就较高了。所以,在存储大容量数据的时候,最好预先指定hashmap的size为2的整数次幂次方。就算不指定的话,也会以大于且最接近指定值大小的2次幂来初始化的。
3、hashmap的resize
那么hashmap什么时候进行扩容呢?当hashmap中的元素个数超过数组大小loadFactor时,就会进行数组扩容,loadFactor的默认值为0.75,也就是说,默认情况下,数组大小为16,那么当hashmap中元素个数超过160.75=12的时候,就把数组的大小扩展为216=32,即扩大一倍,然后重新计算每个元素在数组中的位置,而这是一个非常消耗性能的操作,所以如果我们已经预知hashmap中元素的个数,那么预设元素的个数能够有效的提高hashmap的性能。比如说,我们有1000个元素new HashMap(1000), 但是理论上来讲new HashMap(1024)更合适,不过上面annegu已经说过,即使是1000,hashmap也自动会将其设置为1024。 但是new HashMap(1024)还不是更合适的,因为0.751000 < 1000, 也就是说为了让0.75 * size >1000, 我们必须这样new HashMap(2048)才最合适,既考虑了&的问题,也避免了resize的问题。
4、key的hashcode与equals方法改写
在第一部分hashmap的数据结构中,annegu就写了get方法的过程:首先计算key的hashcode,找到数组中对应位置的某一元素,然后通过key的equals方法在对应位置的链表中找到需要的元素。所以,hashcode与equals方法对于找到对应元素是两个关键方法。
Hashmap的key可以是任何类型的对象,例如User这种对象,为了保证两个具有相同属性的user的hashcode相同,我们就需要改写hashcode方法,比方把hashcode值的计算与User对象的id关联起来,那么只要user对象拥有相同id,那么他们的hashcode也能保持一致了,这样就可以找到在hashmap数组中的位置了。如果这个位置上有多个元素,还需要用key的equals方法在对应位置的链表中找到需要的元素,所以只改写了hashcode方法是不够的,equals方法也是需要改写滴~当然啦,按正常思维逻辑,equals方法一般都会根据实际的业务内容来定义,例如根据user对象的id来判断两个user是否相等。
在改写equals方法的时候,需要满足以下三点:
(1) 自反性:就是说a.equals(a)必须为true。
(2) 对称性:就是说a.equals(b)=true的话,b.equals(a)也必须为true。
(3) 传递性:就是说a.equals(b)=true,并且b.equals(c)=true的话,a.equals(c)也必须为true。
通过改写key对象的equals和hashcode方法,我们可以将任意的业务对象作为map的key(前提是你确实有这样的需要)。
摘自:http://blog.csdn.net/jdjdndhj...
http://www.zhangxinxu.com/wor...
这个写的还是很到位的,大体浏览了一下,没太仔细看,掌握一些大概,以后有空再完全理解。
通过缓存来优化
需要两个组件ClassInfo和ReflectionCache。
ClassInfo主要保存Class对象的信息,主要是方法Map。其中ClassInfo中包括三部分方法的Map: Getter, Setter, Other。Getter是Class的属性的获取方法,Setter是Class的属性的设置方法,Other是其它方法。需要注意的是Getter和Setter的方法需要完全符合Javabean规范(isXXX方法属于Getter方法范围内),其key值是方法对应的属性名。Other方法是除Getter和Setter以外的其它方法。
ReflectionCache组件主要是通过HashMap对ClassInfo进行缓存。缓存的键值是ClassInfo中Class对象的全称。如一个String对象,它缓存的键值就是java.lang.String。并且ReflectionCache提供了几种不同get和put方法来方便用户的操作。
另外ClassInfo的生成需要用到ClassInfoUtils工具。它的主要工作是创建ClassInfo对象,其中创建ClassInfo时可以提供Method Type信息来指定缓存的方法类型(如:所有方法-All、存取器方法-Access、获取方法-Getter和设置方法-Setter)。
5.同步控制
ReflectionCache中ClassInfoMap是一个静变量,那么随之而来的就是HashMap的同步问题。我对ReflectionCache的做了些改进,主要是对put方法的处理。首先HashMap的获取操作(get操作)没有加入同步操作,因此获取的操作是可以并发的。现在的问题在于如果获取不了ClassInfo对象时会要执行设置操作(set操作),此时并发问题随之而来。可能在同一时刻会有很多线程去设置ClassInfo,在第一个设置完ClassInfo的线程结束后,第二个线程应该停止设置ClassInfo。在此需求之上,我们需要对ReflectionCache的put操作上加上同步块,并且让put操作再执行一个额外的操作:返回添加到ClassInfoMap中的ClassInfo,不管它是不是其它线程添加的。因此我们在设置ClassInfo时,可以这样操作:
ClassInfo classInfo = ReflectionCache.putClassInfo(String.class);
select f1 from (select f1 from t1 UNION all select f2 from t2) t3 GROUP BY f1 HAVING COUNT(f1) = 113.hibernate的一二级缓存 14.session和cookie的区别与联系 15.springboot是如何实现,没有配置文件的。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/68671.html
摘要:收集的一些前端面试题从面试题发现不足,进而查漏补缺,比通过面试更难得及各大互联网公司前端笔试面试题篇及各大互联网公司前端笔试面试题篇面试题个和个经典面试题前端开发面试题如何面试前端工程师很重要个变态题解析如何通过饿了么面试轻 收集的一些前端面试题 从面试题发现不足,进而查漏补缺,比通过面试更难得 1 BAT及各大互联网公司2014前端笔试面试题--Html,Css篇 2 BAT...
摘要:收集的一些前端面试题从面试题发现不足,进而查漏补缺,比通过面试更难得及各大互联网公司前端笔试面试题篇及各大互联网公司前端笔试面试题篇面试题个和个经典面试题前端开发面试题如何面试前端工程师很重要个变态题解析如何通过饿了么面试轻 收集的一些前端面试题 从面试题发现不足,进而查漏补缺,比通过面试更难得 1 BAT及各大互联网公司2014前端笔试面试题--Html,Css篇 2 BAT...
阅读 2567·2021-11-02 14:39
阅读 4267·2021-10-11 10:58
阅读 1371·2021-09-06 15:12
阅读 1786·2021-09-01 10:49
阅读 1281·2019-08-29 18:31
阅读 1851·2019-08-29 16:10
阅读 3286·2019-08-28 18:21
阅读 821·2019-08-26 10:42