Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/347.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java HashMap调整大小方法实现细节_Java_Java 8_Hashmap_Hashcode - Fatal编程技术网

Java HashMap调整大小方法实现细节

Java HashMap调整大小方法实现细节,java,java-8,hashmap,hashcode,Java,Java 8,Hashmap,Hashcode,正如标题所示,这是一个关于HashMap#resize中的实现细节的问题,此时内部数组的大小增加了一倍。 这有点罗嗦,但我真的试图证明我已经尽了最大的努力理解了这一点 当此特定存储桶/存储箱中的条目以链接的方式存储时,会发生这种情况-因此具有准确的顺序,在问题的上下文中,这一点很重要 通常,resize也可以从其他地方调用,但我们只看一下这种情况 假设您将这些字符串作为键放入HashMap(右侧是HashMap#hash之后的hashcode——这是内部重新散列)。是的,这些字符串是仔细生成的,

正如标题所示,这是一个关于
HashMap#resize
中的实现细节的问题,此时内部数组的大小增加了一倍。 这有点罗嗦,但我真的试图证明我已经尽了最大的努力理解了这一点

当此特定存储桶/存储箱中的条目以
链接的方式存储时,会发生这种情况-因此具有准确的顺序,在问题的上下文中,这一点很重要

通常,
resize
也可以从其他地方调用,但我们只看一下这种情况

假设您将这些字符串作为键放入
HashMap
(右侧是
HashMap#hash
之后的
hashcode
——这是内部重新散列)。是的,这些字符串是仔细生成的,而不是随机生成的

 DFHXR - 11111
 YSXFJ - 01111 
 TUDDY - 11111 
 AXVUH - 01111 
 RUTWZ - 11111
 DEDUC - 01111
 WFCVW - 11111
 ZETCU - 01111
 GCVUR - 11111 
这里有一个简单的模式需要注意-最后4位对于所有键都是相同的-这意味着当我们插入其中8个键(总共9个)时,它们将在同一个桶中结束;在第9个
HashMap#put
,将调用
resize

因此,如果当前在
HashMap
中有8个条目(具有上面的一个键),则表示该映射中有16个bucket,它们的最后4位键决定了条目的结束位置

我们把第九把钥匙。此时,点击
TREEIFY_阈值
,并调用
resize
。箱子加倍到
32
,并且钥匙中的一个多位决定了条目的位置(因此,现在是5位)

最终达到这段代码(当<代码>调整大小发生时):

这样做的目的是检查下一位(在本例中是第5位)是否实际为零——如果为零,则意味着该条目将保留在原来的位置;如果不是,它将在新箱子中以二次方偏移量移动

最后一个问题是:resize中的那段代码是经过精心设计的,因此它保留了该容器中条目的顺序

因此,将这9个键放入
HashMap
后,顺序将是:

DFHXR -> TUDDY -> RUTWZ -> WFCVW -> GCVUR (one bin)

YSXFJ -> AXVUH -> DEDUC -> ZETCU (another bin)
为什么要保留
HashMap
中某些条目的顺序。
Map
中的顺序与细节或细节一样糟糕

地图上的顺序真的很糟糕[…]

这还不错,(用学术术语)随便什么。斯图尔特·马克斯在你发布的第一个链接中写道:

[…]为将来的实施更改保留灵活性[…]


这意味着(据我所知),现在实现恰好保持了顺序,但在未来,如果找到更好的实现,将使用它来保持顺序或不保持顺序。

在作为链接列表实现的箱子中保持顺序有两个常见原因:

一种是通过增加(或减少)散列值来维持顺序。 这意味着在搜索bin时,只要当前项大于(或小于,如果适用)正在搜索的哈希值,就可以停止搜索

另一种方法是在访问时将条目移动到bucket的前面(或更靠近前面),或者只是将条目添加到前面。这适用于刚被访问的元素被访问的概率很高的情况

我已经查看了JDK-8的源代码,它似乎(至少在大部分情况下)正在执行后者的被动版本(添加到前端):

诚然,您永远不应该依赖不保证迭代顺序的容器的迭代顺序,但这并不意味着如果它是结构化的,就不能利用它来提高性能。还请注意,类的实现具有特权,可以以该类用户不应采用的正式方式利用其实现的细节

如果您查看源代码并了解它是如何实现和利用的,那么您就是在冒险。如果实现者这么做了,那就不同了

注: 我有一个算法的实现,它严重依赖于名为Hashlife的哈希表。使用这个模型,有一个二次幂的散列表,因为(a)你可以通过位屏蔽(&mask)而不是除法来获取条目,(b)由于你只需要“解压”每个散列箱,所以重置被简化了

基准测试表明,通过在访问时主动将模式移动到其存储箱的前面,该算法可以获得约20%的收益


该算法基本上利用了细胞自动机中的重复结构,这种结构很常见,因此,如果你看到了一种模式,那么再次看到它的几率很高

设计考虑事项已记录在同一源文件中,在中的代码注释中


由于通过迭代器删除映射不会触发调整大小,因此在
resize
中保留特定顺序的原因是“为了更好地保留局部性,并稍微简化拆分的处理”,同时在策略方面保持一致。

嘿,评论已经说过:“新表中的二次方偏移"@Nicolai说实话,java-8和java-9标签在这里只是因为我真的希望引起一些可能知道答案的特定用户的注意…@Nicolai,这也是java-8以及以后的
HashMap
@Eugene所特有的,根据这种逻辑,讨论java 9中未删除的内容的所有问题都可以被删除标记为
java-9
。这将使标记无法作为Java9中引入的特性的标记。如果你仍然不同意,我们可能想讨论一下。@Nicolai是的,这可能是有道理的。我的编辑请阅读评论,因为这并没有回答问题。你的意思是有更好的解释,因此我的答案是错误的吗?(或者你指的是其他评论,有很多评论…)这没有错——例如,任何人都可以自由投票;但我不觉得它能回答我的问题好的没问题,但你认为它能回答我的问题吗
 if ((e.hash & oldCap) == 0) 
DFHXR -> TUDDY -> RUTWZ -> WFCVW -> GCVUR (one bin)

YSXFJ -> AXVUH -> DEDUC -> ZETCU (another bin)
* When bin lists are treeified, split, or untreeified, we keep 
* them in the same relative access/traversal order (i.e., field 
* Node.next) to better preserve locality, and to slightly 
* simplify handling of splits and traversals that invoke 
* iterator.remove. When using comparators on insertion, to keep a 
* total ordering (or as close as is required here) across 
* rebalancings, we compare classes and identityHashCodes as 
* tie-breakers.