public HashMap(int initialCapacity,float loadFactor)
方法中
//容量极限
threshold=(int)(capacity*loadFacor);
addEntry(int hash,K key,V value,int bucketIndex)
方法zhong
//如果Map中的键值对超过容量极限
if(size++>=threshold)
resize(2*table.length);
loadFactor可以看成是容量极限(threshold)/实际容量(capacity)=键值最高对数/实际容量
为什么增大负载因子可以减少Hash表所占用的内存空间,但会增加查询数据的时间开销。
为什么减少负载因子可以提高查询数据的性能,但会减低Hash表所占用的内存空间。
看不出来啊。。。。。。
13 个解决方案
#1
如果负载因子是默认的0.75,HashMap(16)的时候,占16个内存空间,实际上只用到了12个,超过12个就扩容。
如果负载因子是1的话,HashMap(16)的时候,占16个内存空间,实际上会填满16个以后才会扩容。
但是,用到的空间越多,hashcode重复的可能性就越大,同一个空间里面的元素数目就可能会增加,会增加查找的时间。
如果负载因子是1的话,HashMap(16)的时候,占16个内存空间,实际上会填满16个以后才会扩容。
但是,用到的空间越多,hashcode重复的可能性就越大,同一个空间里面的元素数目就可能会增加,会增加查找的时间。
#2
那为什么说增大负载因子可以减少hash表的内存,
不是容量极限扩大了吗?
不是容量极限扩大了吗?
#3
如果负载因子是0.75,hashmap(16)最多可以存储12个元素,想存第16个就得扩容成32。
如果负载因子是1,hashmap(16)最多可以存储16个元素。
同样存16个元素,一个占了32个空间,一个占了16个空间的内存。
如果负载因子是1,hashmap(16)最多可以存储16个元素。
同样存16个元素,一个占了32个空间,一个占了16个空间的内存。
#4
减少时相对减少.用到的时候才去扩展的意思.
#5
那假如现在有132个键值对(12的倍数)
负载因子是0.75 占 132个空间
负载因子1 占144个空间
负载因子大的反而多占用空间,
怎么能得出增大负载因子可以减少Hash表所占用的内存空间这个结论呢.
#6
实际容量 = 最大容量 * 负载因子,如果最大容量不变的情况下增大负载因子,当然可以增加实际容量,如果负载因子大了会增加哈希冲突发生的概率,LZ看看哈希表就清楚了
#7
“但会增加查询数据的时间开销”,这个就是由于需要解决哈希冲突需要的事件开销
#8
好吧 先说说我的理解,错误的请纠正
我现在的理解,增大负载因子可以减少hash表的内存空间,
这个内存空间指的是初始容量initialCapacity构造hash表所产生的空间,
在容量极限(threshold)一定时,负载因子越大,则实际容量(capacity)越小,即所需初始容量(initialCapacity)越小,则初始构造的hash表所占空间越小。
增大负载因子会增加查询数据的时间开销,
肯定是多产生了Entry链。
在发生"hash冲突"情况下(即hashCode相同 equals不同),会产生Entry链,
当要查找时,遍历Entry链花的时间多。
但是这个和空间有什么关系?
Entry即键值对数组所占的空间少,为什么发生的hash冲突变多了?
我现在的理解,增大负载因子可以减少hash表的内存空间,
这个内存空间指的是初始容量initialCapacity构造hash表所产生的空间,
在容量极限(threshold)一定时,负载因子越大,则实际容量(capacity)越小,即所需初始容量(initialCapacity)越小,则初始构造的hash表所占空间越小。
增大负载因子会增加查询数据的时间开销,
肯定是多产生了Entry链。
在发生"hash冲突"情况下(即hashCode相同 equals不同),会产生Entry链,
当要查找时,遍历Entry链花的时间多。
但是这个和空间有什么关系?
Entry即键值对数组所占的空间少,为什么发生的hash冲突变多了?
#9
Entry数组所占空间变少,为什么hash冲突变多了?
hashCode的计算方法没变,不应该多产生Entry链啊。
hashCode的计算方法没变,不应该多产生Entry链啊。
#10
楼主可以先看看HashSet这个数据结构,比HashMap稍微简单一些。
采用Hash散列进行存储,主要就是为了提高检索速度。
众所周知,
有序数组存储数据,对数据的检索效率会很高,但是,插入和删除会有瓶颈产生。
链表存储数据,通常只能采用逐个比较的方法来检索数据(查找数据),但是,插入和删除的效率很高。
于是,将两者结合,取长补短,优势互补一下,就产生哈希散列这种存储方式。
具体是怎么样的呢?
我们可以理解成,在链表存储的基础上,对链表结构进行的一项改进。
我们将一个大的链表,拆散成几个或者几十个小的链表。
每个链表的表头,存放到一个数组里面。
这样,在从大链表拆分成小链表的时候就有讲究了。
我们按照什么规则来将一个大链表中的数据,分散存放到不同的链表中呢?
在计算机当中,肯定是要将规则数量化的,也就是说,这个规则,一定要是个数字,这样才比较好操作。
比如,按照存放时间,每5分钟一个时间段,将相同时间段存放的数据,放到同一个链表里面;
或者,将数据排序,每5个数据形成一个链表;
等等,等等,还有好多可以想象得到的方法。
但是,这些方法都会存在一些不足之处。
我们就在想了,如果存放的数据,都是整数就好了。
这样,我可以创建一个固定大小的数组,比如50个大小,然后,让数据(整数)对50进行取余运算,
然后,这些数据,自然就会被分成50个链表了,每个链表可以是无序的,反正链表要逐个比较进行查询。
如果,我一个有200个数据,分组后,平均每组也就4个数据,那么,链表比较,平均也就比较4次就好了。
但是,实际上,我们存放的数据,通常都不是整数。
所以,我们需要将数据对象映射成整数的一个算法。
HashCode方法,应运而生了。
每个数据对象,都会对应一个HashCode值,通过HashCode我们可以将对象分组存放到不同的队列里。
这样,在检索的时候,就可以减少比较次数。
在实际使用当中,HashCode方法、数组的大小 以及 数据对象的数量,这三者对检索的性能有着巨大的影响。
1.如果数组大小为1,那和链表存储没有什么区别了,
而且,还多了一步计算HashCode的时间,所以,数组不能太小,太小查询费时间。
2.如果我只存放1个数据对象,数组又非常大,那么,数组所占的内存空间,就比数据对象占的空间还大,
这样,少量数据对象,巨大的数组,虽然能够使检索速度,但是,浪费了很多内存空间。
3.如果所有对象的HashCode值都是相同的数,
那么,无论数组有多大,这些数据都会保存到同一个链表里面,
一个好的HashCode算法,可以使存放的数据,有较好的分散性,
在实际的实现当中,HashSet和HashMap都对数据对象产生的HashCode进行了二次散列处理,
使得数据对象具有更好的分散性。
上述的1和2就是楼主的问题。
以上纯属个人见解,请大家多多指教。
采用Hash散列进行存储,主要就是为了提高检索速度。
众所周知,
有序数组存储数据,对数据的检索效率会很高,但是,插入和删除会有瓶颈产生。
链表存储数据,通常只能采用逐个比较的方法来检索数据(查找数据),但是,插入和删除的效率很高。
于是,将两者结合,取长补短,优势互补一下,就产生哈希散列这种存储方式。
具体是怎么样的呢?
我们可以理解成,在链表存储的基础上,对链表结构进行的一项改进。
我们将一个大的链表,拆散成几个或者几十个小的链表。
每个链表的表头,存放到一个数组里面。
这样,在从大链表拆分成小链表的时候就有讲究了。
我们按照什么规则来将一个大链表中的数据,分散存放到不同的链表中呢?
在计算机当中,肯定是要将规则数量化的,也就是说,这个规则,一定要是个数字,这样才比较好操作。
比如,按照存放时间,每5分钟一个时间段,将相同时间段存放的数据,放到同一个链表里面;
或者,将数据排序,每5个数据形成一个链表;
等等,等等,还有好多可以想象得到的方法。
但是,这些方法都会存在一些不足之处。
我们就在想了,如果存放的数据,都是整数就好了。
这样,我可以创建一个固定大小的数组,比如50个大小,然后,让数据(整数)对50进行取余运算,
然后,这些数据,自然就会被分成50个链表了,每个链表可以是无序的,反正链表要逐个比较进行查询。
如果,我一个有200个数据,分组后,平均每组也就4个数据,那么,链表比较,平均也就比较4次就好了。
但是,实际上,我们存放的数据,通常都不是整数。
所以,我们需要将数据对象映射成整数的一个算法。
HashCode方法,应运而生了。
每个数据对象,都会对应一个HashCode值,通过HashCode我们可以将对象分组存放到不同的队列里。
这样,在检索的时候,就可以减少比较次数。
在实际使用当中,HashCode方法、数组的大小 以及 数据对象的数量,这三者对检索的性能有着巨大的影响。
1.如果数组大小为1,那和链表存储没有什么区别了,
而且,还多了一步计算HashCode的时间,所以,数组不能太小,太小查询费时间。
2.如果我只存放1个数据对象,数组又非常大,那么,数组所占的内存空间,就比数据对象占的空间还大,
这样,少量数据对象,巨大的数组,虽然能够使检索速度,但是,浪费了很多内存空间。
3.如果所有对象的HashCode值都是相同的数,
那么,无论数组有多大,这些数据都会保存到同一个链表里面,
一个好的HashCode算法,可以使存放的数据,有较好的分散性,
在实际的实现当中,HashSet和HashMap都对数据对象产生的HashCode进行了二次散列处理,
使得数据对象具有更好的分散性。
上述的1和2就是楼主的问题。
以上纯属个人见解,请大家多多指教。
#11
#12
当你调用map.get("abc")时,是按hashcode进行检索的,可是hashcode如何得到呢?
hashcode是存放在一个数组里,hashcode的访问是按照数组的序号来访问的,这里是计算hashcode的,
static int hash(int h) {
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
而这里,就是计算每个hashcode的数组序号的,length就是那个数组的长度。
static int indexFor(int h, int length) {
return h & (length-1);
}
如果有不同的hashcode被放在了相同的indexFor下面,就产生hash冲突,就是e!=null,解决hash冲突有许多种方法,而这里是往下继续查找,直到找到为null的值。
建议LZ看看hash表数据结构。
hashcode是存放在一个数组里,hashcode的访问是按照数组的序号来访问的,这里是计算hashcode的,
static int hash(int h) {
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
而这里,就是计算每个hashcode的数组序号的,length就是那个数组的长度。
static int indexFor(int h, int length) {
return h & (length-1);
}
如果有不同的hashcode被放在了相同的indexFor下面,就产生hash冲突,就是e!=null,解决hash冲突有许多种方法,而这里是往下继续查找,直到找到为null的值。
建议LZ看看hash表数据结构。
#13
那个负载因子(load factor)是时间和空间的一种折中 如果增大load factor 可以减少hash表所占用的内存空间,但会增加查询的时间开销
#1
如果负载因子是默认的0.75,HashMap(16)的时候,占16个内存空间,实际上只用到了12个,超过12个就扩容。
如果负载因子是1的话,HashMap(16)的时候,占16个内存空间,实际上会填满16个以后才会扩容。
但是,用到的空间越多,hashcode重复的可能性就越大,同一个空间里面的元素数目就可能会增加,会增加查找的时间。
如果负载因子是1的话,HashMap(16)的时候,占16个内存空间,实际上会填满16个以后才会扩容。
但是,用到的空间越多,hashcode重复的可能性就越大,同一个空间里面的元素数目就可能会增加,会增加查找的时间。
#2
那为什么说增大负载因子可以减少hash表的内存,
不是容量极限扩大了吗?
不是容量极限扩大了吗?
#3
如果负载因子是0.75,hashmap(16)最多可以存储12个元素,想存第16个就得扩容成32。
如果负载因子是1,hashmap(16)最多可以存储16个元素。
同样存16个元素,一个占了32个空间,一个占了16个空间的内存。
如果负载因子是1,hashmap(16)最多可以存储16个元素。
同样存16个元素,一个占了32个空间,一个占了16个空间的内存。
#4
减少时相对减少.用到的时候才去扩展的意思.
#5
那假如现在有132个键值对(12的倍数)
负载因子是0.75 占 132个空间
负载因子1 占144个空间
负载因子大的反而多占用空间,
怎么能得出增大负载因子可以减少Hash表所占用的内存空间这个结论呢.
#6
实际容量 = 最大容量 * 负载因子,如果最大容量不变的情况下增大负载因子,当然可以增加实际容量,如果负载因子大了会增加哈希冲突发生的概率,LZ看看哈希表就清楚了
#7
“但会增加查询数据的时间开销”,这个就是由于需要解决哈希冲突需要的事件开销
#8
好吧 先说说我的理解,错误的请纠正
我现在的理解,增大负载因子可以减少hash表的内存空间,
这个内存空间指的是初始容量initialCapacity构造hash表所产生的空间,
在容量极限(threshold)一定时,负载因子越大,则实际容量(capacity)越小,即所需初始容量(initialCapacity)越小,则初始构造的hash表所占空间越小。
增大负载因子会增加查询数据的时间开销,
肯定是多产生了Entry链。
在发生"hash冲突"情况下(即hashCode相同 equals不同),会产生Entry链,
当要查找时,遍历Entry链花的时间多。
但是这个和空间有什么关系?
Entry即键值对数组所占的空间少,为什么发生的hash冲突变多了?
我现在的理解,增大负载因子可以减少hash表的内存空间,
这个内存空间指的是初始容量initialCapacity构造hash表所产生的空间,
在容量极限(threshold)一定时,负载因子越大,则实际容量(capacity)越小,即所需初始容量(initialCapacity)越小,则初始构造的hash表所占空间越小。
增大负载因子会增加查询数据的时间开销,
肯定是多产生了Entry链。
在发生"hash冲突"情况下(即hashCode相同 equals不同),会产生Entry链,
当要查找时,遍历Entry链花的时间多。
但是这个和空间有什么关系?
Entry即键值对数组所占的空间少,为什么发生的hash冲突变多了?
#9
Entry数组所占空间变少,为什么hash冲突变多了?
hashCode的计算方法没变,不应该多产生Entry链啊。
hashCode的计算方法没变,不应该多产生Entry链啊。
#10
楼主可以先看看HashSet这个数据结构,比HashMap稍微简单一些。
采用Hash散列进行存储,主要就是为了提高检索速度。
众所周知,
有序数组存储数据,对数据的检索效率会很高,但是,插入和删除会有瓶颈产生。
链表存储数据,通常只能采用逐个比较的方法来检索数据(查找数据),但是,插入和删除的效率很高。
于是,将两者结合,取长补短,优势互补一下,就产生哈希散列这种存储方式。
具体是怎么样的呢?
我们可以理解成,在链表存储的基础上,对链表结构进行的一项改进。
我们将一个大的链表,拆散成几个或者几十个小的链表。
每个链表的表头,存放到一个数组里面。
这样,在从大链表拆分成小链表的时候就有讲究了。
我们按照什么规则来将一个大链表中的数据,分散存放到不同的链表中呢?
在计算机当中,肯定是要将规则数量化的,也就是说,这个规则,一定要是个数字,这样才比较好操作。
比如,按照存放时间,每5分钟一个时间段,将相同时间段存放的数据,放到同一个链表里面;
或者,将数据排序,每5个数据形成一个链表;
等等,等等,还有好多可以想象得到的方法。
但是,这些方法都会存在一些不足之处。
我们就在想了,如果存放的数据,都是整数就好了。
这样,我可以创建一个固定大小的数组,比如50个大小,然后,让数据(整数)对50进行取余运算,
然后,这些数据,自然就会被分成50个链表了,每个链表可以是无序的,反正链表要逐个比较进行查询。
如果,我一个有200个数据,分组后,平均每组也就4个数据,那么,链表比较,平均也就比较4次就好了。
但是,实际上,我们存放的数据,通常都不是整数。
所以,我们需要将数据对象映射成整数的一个算法。
HashCode方法,应运而生了。
每个数据对象,都会对应一个HashCode值,通过HashCode我们可以将对象分组存放到不同的队列里。
这样,在检索的时候,就可以减少比较次数。
在实际使用当中,HashCode方法、数组的大小 以及 数据对象的数量,这三者对检索的性能有着巨大的影响。
1.如果数组大小为1,那和链表存储没有什么区别了,
而且,还多了一步计算HashCode的时间,所以,数组不能太小,太小查询费时间。
2.如果我只存放1个数据对象,数组又非常大,那么,数组所占的内存空间,就比数据对象占的空间还大,
这样,少量数据对象,巨大的数组,虽然能够使检索速度,但是,浪费了很多内存空间。
3.如果所有对象的HashCode值都是相同的数,
那么,无论数组有多大,这些数据都会保存到同一个链表里面,
一个好的HashCode算法,可以使存放的数据,有较好的分散性,
在实际的实现当中,HashSet和HashMap都对数据对象产生的HashCode进行了二次散列处理,
使得数据对象具有更好的分散性。
上述的1和2就是楼主的问题。
以上纯属个人见解,请大家多多指教。
采用Hash散列进行存储,主要就是为了提高检索速度。
众所周知,
有序数组存储数据,对数据的检索效率会很高,但是,插入和删除会有瓶颈产生。
链表存储数据,通常只能采用逐个比较的方法来检索数据(查找数据),但是,插入和删除的效率很高。
于是,将两者结合,取长补短,优势互补一下,就产生哈希散列这种存储方式。
具体是怎么样的呢?
我们可以理解成,在链表存储的基础上,对链表结构进行的一项改进。
我们将一个大的链表,拆散成几个或者几十个小的链表。
每个链表的表头,存放到一个数组里面。
这样,在从大链表拆分成小链表的时候就有讲究了。
我们按照什么规则来将一个大链表中的数据,分散存放到不同的链表中呢?
在计算机当中,肯定是要将规则数量化的,也就是说,这个规则,一定要是个数字,这样才比较好操作。
比如,按照存放时间,每5分钟一个时间段,将相同时间段存放的数据,放到同一个链表里面;
或者,将数据排序,每5个数据形成一个链表;
等等,等等,还有好多可以想象得到的方法。
但是,这些方法都会存在一些不足之处。
我们就在想了,如果存放的数据,都是整数就好了。
这样,我可以创建一个固定大小的数组,比如50个大小,然后,让数据(整数)对50进行取余运算,
然后,这些数据,自然就会被分成50个链表了,每个链表可以是无序的,反正链表要逐个比较进行查询。
如果,我一个有200个数据,分组后,平均每组也就4个数据,那么,链表比较,平均也就比较4次就好了。
但是,实际上,我们存放的数据,通常都不是整数。
所以,我们需要将数据对象映射成整数的一个算法。
HashCode方法,应运而生了。
每个数据对象,都会对应一个HashCode值,通过HashCode我们可以将对象分组存放到不同的队列里。
这样,在检索的时候,就可以减少比较次数。
在实际使用当中,HashCode方法、数组的大小 以及 数据对象的数量,这三者对检索的性能有着巨大的影响。
1.如果数组大小为1,那和链表存储没有什么区别了,
而且,还多了一步计算HashCode的时间,所以,数组不能太小,太小查询费时间。
2.如果我只存放1个数据对象,数组又非常大,那么,数组所占的内存空间,就比数据对象占的空间还大,
这样,少量数据对象,巨大的数组,虽然能够使检索速度,但是,浪费了很多内存空间。
3.如果所有对象的HashCode值都是相同的数,
那么,无论数组有多大,这些数据都会保存到同一个链表里面,
一个好的HashCode算法,可以使存放的数据,有较好的分散性,
在实际的实现当中,HashSet和HashMap都对数据对象产生的HashCode进行了二次散列处理,
使得数据对象具有更好的分散性。
上述的1和2就是楼主的问题。
以上纯属个人见解,请大家多多指教。
#11
#12
当你调用map.get("abc")时,是按hashcode进行检索的,可是hashcode如何得到呢?
hashcode是存放在一个数组里,hashcode的访问是按照数组的序号来访问的,这里是计算hashcode的,
static int hash(int h) {
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
而这里,就是计算每个hashcode的数组序号的,length就是那个数组的长度。
static int indexFor(int h, int length) {
return h & (length-1);
}
如果有不同的hashcode被放在了相同的indexFor下面,就产生hash冲突,就是e!=null,解决hash冲突有许多种方法,而这里是往下继续查找,直到找到为null的值。
建议LZ看看hash表数据结构。
hashcode是存放在一个数组里,hashcode的访问是按照数组的序号来访问的,这里是计算hashcode的,
static int hash(int h) {
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
而这里,就是计算每个hashcode的数组序号的,length就是那个数组的长度。
static int indexFor(int h, int length) {
return h & (length-1);
}
如果有不同的hashcode被放在了相同的indexFor下面,就产生hash冲突,就是e!=null,解决hash冲突有许多种方法,而这里是往下继续查找,直到找到为null的值。
建议LZ看看hash表数据结构。
#13
那个负载因子(load factor)是时间和空间的一种折中 如果增大load factor 可以减少hash表所占用的内存空间,但会增加查询的时间开销