mysql能存储hashmap吗_HashMap是怎样存储和快速查找的

参考:廖雪峰老师的java教程

我们都知道Map是一种键值对映射表,可以通过key快速查找对应的value.

以HashMap为例,观察下面的代码:

Map map = new HashMap<>();

map.put("apple",12);

map.put("pear",10);

map.put("origin",5);

map.get("apple"); //12

HashMap之所以能根据key直接拿到value,,原因是它内部通过空间换时间的方法,用一个大数组存储所有的value,并根据key直接计算出value应该存储在那个索引:

┌───┐

0 │ │

├───┤

1 │ ●─┼───> Person("Xiao Ming")

├───┤

2 │ │

├───┤

3 │ │

├───┤

4 │ │

├───┤

5 │ ●─┼───> Person("Xiao Hong")

├───┤

6 │ ●─┼───> Person("Xiao Jun")

├───┤

7 │ │

└───┘

如果key的值为"a",计算得到的索引总为 1 ,因此返回value为Person("Xiao Ming"),如果key的值为 "b",计算得到的索引总为5,因此返回value为Person("Xiao Hong"),这样就不必遍历整个数组,即可以直接读取key对应的value.

当我们使用 key 存取value的时候,就会引起一个问题:

我们放入map的可以是字符串 "a",但是,当我们获取Map的value时,不一定就是放入的那个key对象.

换句话讲,两个key应该是内容相同,但不一定是同一个对象.

@Test

public void testHashMap(){

String key1 = "a";

Map map = new HashMap<>();

map.put(key1,123);

String key2 = new String("a");

int i = map.get(key2);

System.out.println(i); //123

System.out.println(key1 == key2); // false 说明key1和key2是两个对象

System.out.println(key1.equals(key2)); //true 说明key1的内容和key2相同

}

因为在Map内部,对key做比较是通过equals()实现的,这一点和List查找元素需要正确覆写equals()是一样的,即正确的Map必须保证:作为key的对象必须正确覆写equals方法.

我们经常使用String 作为 key,因为String 已经正确覆写equals方法.但如果我们放入的key 是一个自己写的类,就必须保证正确覆写equals方法.

我们再思考一下 HashMap 为什么能通过 key 直接计算出 value 存储的索引.相同的key 对象(使用equals判断时返回true)必须要计算出相同的索引,否则,相同的key每次取出value就不一定对.

通过key计算索引的方式就是调用key对象的hashCode()方法,它返回一个int整数.HashMap正是通过这个方法直接定位key对应的value的索引,继而直接返回value.

因此,正确使用Map必须保证:

作为key的对象必须正确覆写equals()方法,相等的两个key实例调用equals()必须返回True;

作为key的对象还必须正确覆写hashCode()方法,且hashCode()方法要严格遵循一下规范:

如果两个对象相等,则两个对象的hashCode()必须相等;

如果两个对象不相等,则两个对象的hashCode()尽量不要相等.

扩展

既然HashMap内部使用了数组,通过计算key的HashCode()直接定位value所在的索引,那么第一个问题就来了:hashCode()返回的int返回高达±21亿,先不考虑负数,HashMap内部使用的数组得有多大?

实际上 HashMap初始化默认的数组大小只有 16,任何key,无论它的hashCode()有多大,都可以简单地通过:

int index = key.hashCode() & 0xf; //0xf = 15

把索引确定为0~15之间,即永远不会超出数组范围,上述算法只是一种最简单的实现.

第二个问题:如果添加超过16个key-value到HashMap,数组不够用怎么办?

添加超过一定数量的key-value时,HashMap会在内部自动扩容,每次扩容一倍,即长度为16的数组扩展为长度为32,相应的,需要重新计算hashCode() 索引位置.例如:对长度为32的数组计算hashCode()对应的索引,计算方式要改为:

int index = key.hashCode() & 0x1f; // 0x1f = 31

由于扩容会导致重新分布已有的key-value,所以,频繁扩容对HashMap的性能影响很大.如果我们确定要使用一个10000个key-value的HashMap,更好的方式是创建HashMap时就指定容量:

Map map = new HashMap<>(10000);

虽然指定容量是10000,但是HashMap内部的数组长度总是 \(2^n\),因此,实际数组长度被初始化为比10000大的16384(\(2^14\))

最后一个为题:如果两个不相同的key,例如"a" 和"b",他们的hashCode()恰好是相同的(这种情况是完全有可能的,因为不相等的两个实例,只要求hashCode()尽量不相等),那么,当我们放入:

map.put("a", new Person("Xiao Ming"));

map.put("b", new Person("Xiao Hong"));

由于计算出的数组索引相同,后面放入"Xiao Hong"会不会把"Xiao Ming"覆盖了?

当然不会!使用Map的时候,只要key不相同,他们映射的value就不会互不干扰.但是,在hashMap内部,确实可能存在不同的key,映射到相同的hashCode(),即相同的数组索引上怎么办?

我们就假设"a" 和"b" 这两个key 最终计算出的索引都是5,那么,在HashMap的数组中,实际存储的不是一个Person实例,而是一个List,它包含 两个Entry,一个是"a"的映射,一个是"b"的映射:

┌───┐

0 │ │

├───┤

1 │ │

├───┤

2 │ │

├───┤

3 │ │

├───┤

4 │ │

├───┤

5 │ ●─┼───> List>

├───┤

6 │ │

├───┤

7 │ │

└───┘

在查找的时候,例如:

Person p = map.get("a");

HashMap内部通过"a"找到的实际上是List>,它还需要遍历这个list,并找到一个Entry,它的key字段是"a",才能返回对应的Person实例.

我们把不同的key具有相同的hashCode()的情况称之为哈希冲突.在冲突的时候,一种最简单的解决办法是用List存储hashCode()相同的key-value.显然冲突的概率越大,这个list就越长,map的get()方法效率就越低,这就是为什么要尽量满足条件二:

如果两个对象不相等,则两个对象的hashCode()尽量不要相等

hashCode()方法编写得号,HashMap的工作效率就越高.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【优质项目推荐】 1、项目代码均经过严格本地测试,运行OK,确保功能稳定后才上传平台。可放心下载并立即投入使用,若遇到任何使用问题,随时欢迎私信反馈与沟通,博主会第一时间回复。 2、项目适用于计算机相关专业(如计科、信息安全、数据科学、人工智能、通信、物联网、自动化、电子信息等)的在校学生、专业教师,或企业员工,小白入门等都适用。 3、该项目不仅具有很高的学习借鉴价值,对于初学者来说,也是入门进阶的绝佳选择;当然也可以直接用于 毕设、课设、期末大作业或项目初期立项演示等。 3、开放创新:如果您有一定基础,且热爱探索钻研,可以在此代码基础上二次开发,进行修改、扩展,创造出属于自己的独特应用。 欢迎下载使用优质资源!欢迎借鉴使用,并欢迎学习交流,共同探索编程的无穷魅力! 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值