热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

JavaHashMap内部数据结构在重新哈希处理期间如何变化?

如何解决《JavaHashMap内部数据结构在重新哈希处理期间如何变化?》经验,为你挑选了1个好方法。

我正在尝试编写演示代码,以显示当地图大小超过负载因子阈值时,Hashmap中正在发生重新哈希。我如何证明内部进行了哈希处理。我也想证明即使在重新哈希期间将旧条目移到新存储桶中,我也可以使用旧键来获取旧元素(让我知道我的假设是正确的)。下面的示例代码。

import java.util.*;

    class RehashDemo{

        public static void main(String[] args){
            Map numbers = new HashMap<>(10);
            for(int i = 0; i<10;i++){
                numbers.put(i,i+"");
            }
            System.out.println(numbers);

            for(int j = 15; j<=20;j++){
                numbers.put(j,j+"");
            }
            System.out.println(numbers);

        }


    }

Stuart Marks.. 6

编写演示哈希的程序并不难,但是您必须了解很多有关HashMap的内部组织,对象的哈希码如何生成,哈希码如何与HashMap的内部结构相关以及如何影响迭代顺序的知识。

简而言之,HashMap由一系列存储桶(“表”)组成。每个存储桶都是键值对的链接列表。将一对其键哈希值添加到已被占用的存储桶中,将其添加到该存储桶的链接列表的末尾。铲斗通过调用键的确定hashCode()方法,与它的高次异或它16位右无符号移了16(参照源),然后取表大小的模量。由于表的大小始终是2的幂,因此本质上是与(tablesize-1)的掩码进行“与”运算。Integer对象的哈希码只是其整数值。(来源)。最后,HashMap的迭代顺序依次遍历每个存储桶,还依次遍历每个存储桶内的对的链接列表。

毕竟,您可以看到小的整数值将最终出现在相应的存储桶中。例如,Integer.valueOf(0).hashCode()为0。在进行“异和”运算后,它将保持为0,并且任何表大小的模将保持为0。因此,整数0最终存储在存储区0中,整数1最终存储在存储区1中,依此类推。但是不要忘记,存储桶是表大小的模数。因此,如果表大小为8,则整数8将最终出现在存储区0中。

有了这些信息,我们可以用整数键填充HashMap,这些键将最终存储在可预测的存储桶中。让我们创建一个表大小为8且默认加载因子为0.75的HashMap,这意味着我们可以在重新哈希发生之前添加六个映射。

Map map = new HashMap<>(8);
map.put(0, 0);
map.put(8, 8);
map.put(1, 1);
map.put(9, 9);
map.put(2, 2);
map.put(10, 10);

{0=0, 8=8, 1=1, 9=9, 2=2, 10=10}

toString()如上所述,打印出地图(实质上是使用其方法)依次迭代地图。我们可以看到0和8出现在第一个存储桶中,1和9出现在第二个存储桶中,而2和10出现在第三个存储桶中。现在让我们添加另一个条目:

map.put(3, 3);

{0=0, 1=1, 2=2, 3=3, 8=8, 9=9, 10=10}

迭代顺序已更改!添加新映射超出了重新哈希的阈值,因此表大小增加了一倍,达到16。重新哈希已完成,这次模数为16而不是8。而0和8之前都在存储区0中,现在它们位于独立的存储桶,因为有可用存储桶的两倍。与1/9和2/10相同。当表大小为16时,旧表大小为8的每个存储桶中的第二个条目现在散列到其自己的存储桶中。您可以看到这一点,因为迭代顺序地通过存储桶进行,并且每个存储桶中现在都有一个条目。

当然,我仔细选择了整数值,以使表大小为8时不会发生冲突,而表大小为16时不会发生冲突。这使我们可以非常清楚地看到重新哈希。对于更典型的对象,很难预测哈希码(因此也包括存储桶),因此,很难看到冲突以及发生哈希时发生的变化。



1> Stuart Marks..:

编写演示哈希的程序并不难,但是您必须了解很多有关HashMap的内部组织,对象的哈希码如何生成,哈希码如何与HashMap的内部结构相关以及如何影响迭代顺序的知识。

简而言之,HashMap由一系列存储桶(“表”)组成。每个存储桶都是键值对的链接列表。将一对其键哈希值添加到已被占用的存储桶中,将其添加到该存储桶的链接列表的末尾。铲斗通过调用键的确定hashCode()方法,与它的高次异或它16位右无符号移了16(参照源),然后取表大小的模量。由于表的大小始终是2的幂,因此本质上是与(tablesize-1)的掩码进行“与”运算。Integer对象的哈希码只是其整数值。(来源)。最后,HashMap的迭代顺序依次遍历每个存储桶,还依次遍历每个存储桶内的对的链接列表。

毕竟,您可以看到小的整数值将最终出现在相应的存储桶中。例如,Integer.valueOf(0).hashCode()为0。在进行“异和”运算后,它将保持为0,并且任何表大小的模将保持为0。因此,整数0最终存储在存储区0中,整数1最终存储在存储区1中,依此类推。但是不要忘记,存储桶是表大小的模数。因此,如果表大小为8,则整数8将最终出现在存储区0中。

有了这些信息,我们可以用整数键填充HashMap,这些键将最终存储在可预测的存储桶中。让我们创建一个表大小为8且默认加载因子为0.75的HashMap,这意味着我们可以在重新哈希发生之前添加六个映射。

Map map = new HashMap<>(8);
map.put(0, 0);
map.put(8, 8);
map.put(1, 1);
map.put(9, 9);
map.put(2, 2);
map.put(10, 10);

{0=0, 8=8, 1=1, 9=9, 2=2, 10=10}

toString()如上所述,打印出地图(实质上是使用其方法)依次迭代地图。我们可以看到0和8出现在第一个存储桶中,1和9出现在第二个存储桶中,而2和10出现在第三个存储桶中。现在让我们添加另一个条目:

map.put(3, 3);

{0=0, 1=1, 2=2, 3=3, 8=8, 9=9, 10=10}

迭代顺序已更改!添加新映射超出了重新哈希的阈值,因此表大小增加了一倍,达到16。重新哈希已完成,这次模数为16而不是8。而0和8之前都在存储区0中,现在它们位于独立的存储桶,因为有可用存储桶的两倍。与1/9和2/10相同。当表大小为16时,旧表大小为8的每个存储桶中的第二个条目现在散列到其自己的存储桶中。您可以看到这一点,因为迭代顺序地通过存储桶进行,并且每个存储桶中现在都有一个条目。

当然,我仔细选择了整数值,以使表大小为8时不会发生冲突,而表大小为16时不会发生冲突。这使我们可以非常清楚地看到重新哈希。对于更典型的对象,很难预测哈希码(因此也包括存储桶),因此,很难看到冲突以及发生哈希时发生的变化。


推荐阅读
author-avatar
chuntianhuaji
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有