1700445241
if(size++>=threshold)
1700445242
1700445243
resize(2*table.length);
1700445244
1700445245
在插入键值对时,会做长度校验,如果大于或等于阀值(threshold变量),则数组长度增大一倍。不过,默认的阀值是多大的呢?默认是当前长度与加载因子的乘积。
1700445246
1700445247
threshold=(int)(newCapacity*loadFactor);
1700445248
1700445249
默认的加载因子(loadFactor变量)是0.75,也就是说只要HashMap的size大于数组长度的0.75倍时,就开始扩容,经过计算得知(怎么计算的?查找2的N次幂大于40万的最小值即为数组的最大长度,再乘以0.75就是最后一次扩容点,计算的结果是N=19),在Map的size为393216时,符合了扩容条件,于是393216个元素准备开始大搬家,要扩容嘛,那首先要申请一个长度为1048576(当前长度的两倍嘛,2的19次方再乘以2,即2的20次方)的数组,但问题是此时剩余的内存只有7MB了,不足以支撑此运算,于是就报内存溢出了!这是第二个原因,也是最根本的原因。
1700445250
1700445251
这也就解释了为什么还剩余着7MB内存就报内存溢出了。我们再来思考一下ArrayList的扩容策略,它是在小于数组长度的时候才会扩容1.5倍,经过计算得知,ArrayList的size在超过80万后(一次加两个元素,40万的两倍),最近的一次扩容会是在size为1005308时,也就是说,如果程序设置了增加元素的上限为502655,同样会报内存溢出,因为它也要申请一个1507963长度的数组,如果没这么大的地方,就会报错了。
1700445252
1700445253
综合来说,HashMap比ArrayList多了一个层Entry的底层对象封装,多占用了内存,并且它的扩容策略是2倍长度的递增,同时还会依据阀值判断规则进行判断,因此相对于ArrayList来说,它就会先出现内存溢出。
1700445254
1700445255
可能会有读者在想,是不是可以在声明时指定HashMap的默认长度和加载因子来减少此问题的发生。是可以缓解此问题,可以不再频繁地进行数组扩容,但仍然避免不了内存溢出问题,因为键值对的封装对象Entry还是少不了的,内存依然增长较快。
1700445256
1700445257
注意 尽量让HashMap中的元素少量并简单。
1700445258
1700445259
1700445260
1700445261
1700445263
编写高质量代码:改善Java程序的151个建议 建议79:集合中的哈希码不要重复
1700445264
1700445265
在一个列表中查找某值是非常耗费资源的,随机存取的列表是遍历查找,顺序存储列表是链表查找,或者是Collections的二分法查找,但这都不够快,毕竟都是遍历嘛,最快的还要数以Hash开头的集合(如HashMap、HashSet等类)查找,我们以HashMap为例,看看它是如何查找Key值的,代码如下:
1700445266
1700445267
public static void main(String[]args){
1700445268
1700445269
int size=10000;
1700445270
1700445271
List<String>list=new ArrayList<String>(size);
1700445272
1700445273
//初始化
1700445274
1700445275
for(int i=0;i<size;i++){
1700445276
1700445277
list.add(“value”+i);
1700445278
1700445279
}
1700445280
1700445281
//记录开始时间,单位纳秒
1700445282
1700445283
long start=System.nanoTime();
1700445284
1700445285
//开始查找
1700445286
1700445287
list.contains(“value”+(size-1));
1700445288
1700445289
//记录结束时间,单位纳秒
1700445290
[
上一页 ]
[ :1.700445241e+09 ]
[
下一页 ]