Java 索引列表时的最佳HashMap初始容量
我有一个列表(Java 索引列表时的最佳HashMap初始容量,java,algorithm,dictionary,hashmap,Java,Algorithm,Dictionary,Hashmap,我有一个列表(list list),我想使用一个映射(HashMap)根据对象的ID对其对象进行索引。我总是使用list.size()作为HashMap构造函数中的初始容量,如下所示。这是在这种情况下使用的最佳初始容量吗 注意:我永远不会向地图添加更多项目 List<T> list = myList; Map<Integer, T> map = new HashMap<Integer, T>(list.size()); for(T item : list) {
list list
),我想使用一个映射(HashMap
)根据对象的ID对其对象进行索引。我总是使用list.size()
作为HashMap
构造函数中的初始容量,如下所示。这是在这种情况下使用的最佳初始容量吗
注意:我永远不会向地图添加更多项目
List<T> list = myList;
Map<Integer, T> map = new HashMap<Integer, T>(list.size());
for(T item : list) {
map.put(item.getId(), item);
}
List List=myList;
Map Map=newhashmap(list.size());
对于(T项:列表){
map.put(item.getId(),item);
}
你现在做的很好。通过这种方式,您可以确保哈希映射至少有足够的容量用于初始值。如果您有关于哈希映射使用模式的更多信息(例如:是否经常更新?是否经常添加许多新元素?),您可能希望设置更大的初始容量(例如,list.size()*2
),但决不降低。使用探查器确定初始容量是否过早不足
更新
感谢@PaulBellora建议将初始容量设置为(int)Math.ceil(list.size()/loadFactor)
(通常,默认负载系数为0.75),以避免初始调整大小。Guava使用此辅助方法计算默认负载系数0.75
的初始容量,基于一些预期的值数:
/**
* Returns a capacity that is sufficient to keep the map from being resized as
* long as it grows no larger than expectedSize and the load factor is >= its
* default (0.75).
*/
static int capacity(int expectedSize) {
if (expectedSize < 3) {
checkArgument(expectedSize >= 0);
return expectedSize + 1;
}
if (expectedSize < Ints.MAX_POWER_OF_TWO) {
return expectedSize + expectedSize / 3;
}
return Integer.MAX_VALUE; // any large value
}
/**
*返回一个容量,该容量足以防止将映射调整为
*只要其增长不超过预期大小且负载系数>=其
*默认值(0.75)。
*/
静态整数容量(整数预期大小){
如果(预期尺寸<3){
checkArgument(expectedSize>=0);
返回expectedSize+1;
}
if(预期尺寸<整数最大功率(共两个){
返回expectedSize+expectedSize/3;
}
返回Integer.MAX_VALUE;//任何大值
}
参考:
从newHashMapWithExpectedSize
文档中:
创建一个HashMap
实例,该实例具有足够高的“初始容量”,可以
它应该能容纳expectedSize
元素而不增长。这种行为
不能广泛地保证,但观察到这一点对
OpenJDK1.6。也不能保证该方法不正确
无意中过大了返回的映射
根据报告:
在设置初始容量时,应考虑map中的预期条目数及其负载系数,以尽量减少再灰化操作次数。如果初始容量大于最大入口数除以负载系数,则不会发生再灰化操作
这意味着,如果您事先知道HashMap应该存储多少个条目,您可以通过选择适当的初始容量和负载因子来防止重新灰化。然而:
作为一般规则,默认负载系数(.75)在时间和空间成本之间提供了良好的折衷。较高的值会减少空间开销,但会增加查找成本(反映在HashMap类的大多数操作中,包括get和put)
如果您希望避免重新设置
HashMap
,并且您知道没有其他元素将被放入HashMap
,则必须考虑负载系数以及初始容量。载荷系数
每当添加新条目时,即会进行计算以确定是否需要重新灰化,例如,put
放置新的键/值。因此,如果您将初始容量指定为list.size()
,负载系数指定为1,那么它将在最后一次put
之后重新刷新。因此,为了防止重新灰化,请使用负载系数1和容量list.size()+1
编辑
查看HashMap
源代码,如果旧大小达到或超过阈值,它将重新刷新,因此不会在最后一次put
上重新刷新。所以它的容量应该是list.size()
HashMap<Integer, T> map = new HashMap<Integer, T>(list.size(), 1.0);
“capacity”关键字的定义不正确,未按通常预期的方式使用 默认情况下,HashMap的“加载因子”为0.75,这意味着当HashMap中的条目数达到提供容量的75%时,它将调整数组大小并重新加载 例如,如果我这样做:
Map<Integer, Integer> map = new HashMap<>(100);
这将返回list.size()+list.size()的25%,例如,如果我的列表大小为100,则返回133。然后,当地图的大小等于初始容量的75%时,我们会将1添加到地图中,因此如果我们有一个大小为100的列表,我们会将初始容量设置为134,这意味着从列表中添加所有100个条目不会导致地图的任何大小调整
最终结果:
Map<Integer, Integer> map = new HashMap<>(list.size() / 0.75 + 1);
Map Map=newhashmap(list.size()/0.75+1);
如果您不知道负载系数/容量内部构件,请遵循经验法则:
initialCapacityToUse = (Expected No. of elements in map / 0.75) + 1
有了这个初始容量值,在map中存储给定数量的元素时就不会发生重缓存。我建议:1)将变量声明为
map
,而不是HashMap
,2)如果您注意到一个分析器正在给您的性能带来影响,请将这种问题留给JVM,然后开始评估它。@LuiggiMendoza一般说来是的,同意,但这是一个非常常见的用例,我们最好不要重新调整大小“哈希映射至少有足够的容量用于初始值”-如果默认负载系数为0.75,我认为这是不正确的。@PaulBellora初始容量与initialCapacity
参数中指定的大小相同。负载因子是在哈希表的容量(初始值或非初始值)自动增加之前,允许哈希表达到的满度的度量,因此当负载因子为0.75
且初始容量为n
时,将n
值
Map<Integer, Integer> map = new HashMap<>(list.size() / 0.75 + 1);
initialCapacityToUse = (Expected No. of elements in map / 0.75) + 1