HashMap 底层实现原理

1. Java中的集合框架:

2. HashMap 工作原理

(1) 数组的特点:

  • 存储区间是连续的,占用内存严重,空间复杂度很大,时间复杂度为O(1);
  • 优点:随机读取效率很高,原因是数组是连续的(随机访问性强、查找速度快);
  • 缺点:插入和删除数据效率低,因为插入数据时,该位置后面的数据在内存中要向后移动,且数组大小固定不易动态扩展;删除数据也同样涉及到数组中数据在内存中位置的移动问题;

(2) 链表的特点:

  • 存储区间是离散的,占用内存宽松,空间复杂度小,时间复杂度为O(N);
  • 优点:插入和删除数据速度快,内存利用率高,没有大小限制,扩展灵活;
  • 缺点:不能随机查找,每次都是从第一个开始遍历(查询效率低);

(3) 哈希表的特点:

从以上数组和链表的特点可以看出各自都有其优缺点,那么若想鱼和熊掌兼得:查询效率和增删效率都较高,哈希表可以满足!!!

        HashMap 是基于 hashing 原理的,通过 put(key, value) 和 get(key) 方法存储和获取元素。当将键值对传递给 put() 方法时,它调用键对象的 hashCode() 方法来计算 hashCode,然后找到 bucket 位置来存储值对象。在获取对象时,通过键对象的 equals() 方法找到正确的键值对,然后返回值对象。HashMap 使用链表来解决碰撞问题,当发生碰撞时,对象将会存储在链表的下一个节点中。HashMap 在每个链表节点中存储键值对对象。

        当两个不同的键对象的 hashCode 相同时,它们会存储在同一个 bucket 位置的链表中。键对象的 equals() 方法用来找到键值对。

        HashMap 的好处非常多,有时候出于性能方面的考虑,也会经常用到 ConcurrentHashMap。

① map.put(k, v) 实现原理

        第一步:将 key 和 value 封装到 Node 对象当中(节点);

        第二步:底层调用 key 的 hashCode() 方法,得到其 hash 值;

        第三步:通过哈希表函数/哈希算法将 key 的 hash 值转换成数组的下标;

        第四步:判断下标位置上是否有元素,如果没有任何元素,就把 Node 添加到这个位置上;如果下标对应得位置上有链表存在,则会用 key 和 链表上每个节点的 key 进行 equals 比较,若所有的 equals 比较结果都是 false,那么这个新的节点将被添加到链表的末尾;若其中有一个 equals 比较结果返回 true,那么这个节点的 value 将会被覆盖。

② map.get(k) 实现原理

        第一步:调用 key 的 hashCode() 方法得到其哈希值,并通过哈希算法将哈希值转换成数组的下标;

        第二步:通过转换得到的数组下标快速定位到数组中对应的位置。如果这个位置上什么都没有,则返回 null;如果这个位置上有单向链表,那么就用这个 k 和单向链表上的每一个节点的 key 进行 equals 比较,若所有 equals 比较结果都返回 false,则 get(k) 方法返回 null;若其中一个节点的 key 和 get(k) 方法传入的 k 进行 equals 比较的结果返回 true,那么这个节点的 value就是查找的 k 对应的值了,该值将会通过 get(k) 方法返回。

③ 随机增删、查询效率都很高

        原因:增删是在链表上完成的,而查询只需要扫描部分,则效率高。

        HashMap 集合的 key,会先调用 hashCode 和 equals 方法,这两个方法都需要重写。

④ 为什么放在 HashMap 集合 key 部分的元素需要重写 equals 方法?

        因为 equals 默认比较的是两个对象的内存地址;

3. HashMap 总结

        无序、key不可重复可有一个为空,value可重复可有多个空值。

        无序,因为其中的元素不一定是存放在哪个单向链表上的,因此插入顺序和取出也不一样;

        key不可重复,在插入元素时,会使用 equals 方法来保证 HashMap 集合的 key 不重复,如果 key 重复则对应的 value 就会被覆盖掉。

        存放在 HashMap 集合 key 部分的元素其实就是存放在 HashSet 集合中,则 HashSet 集合也需要重写 equals 和 hashCode 方法。

        HashMap 集合的默认初始化容量为16,默认加载因子为0.75;这个默认加载因子是,当 HashMap 集合底层数组的容量达到75%时,数组就开始扩容。HashMap 集合初始化容量是2的倍数,为了达到散列均匀,提高 HashMap 集合的存取效率。

4. HashMap 和 Hashtable 的区别

        HashMap 和 Hashtable 都实现了 Map 接口,在决定使用哪一个之前要先明确二者之间的区别;主要的区别在于:线程安全性、同步(synchronization)、速度。

        (1) HashMap 是非 synchronized 的,非线程安全的;而 Hashtable 是 synchronized 的,线程安全的;

                在多个线程访问HashTable时,不需要自己为它的方法实现同步,而HashMap就必须为之提供额外的同步;

                因为 Hashtable 是线程安全的,多个线程可以共享同一个 Hashtable;而如果使用 HashMap 时没有提供额外的同步方法的话,多线程是不能共享 HashMap 的;

                在Java5中,提供了 ConcurrentHashMap ,它是 Hashtable 的替代,比 Hashtable 的扩展性更好。

        (2) HashMap 可以接受 null (HashMap 可以接受为 null 的键(key)和值(value));而 Hashtable则不允许;

        (3) 由于 HashMap 是非线程安全的,Hashtable 是线程安全的,因此,HashMap 的执行效率要高于 Hashtable;

        (4) HashMap 和 Hashtable 的另一个区别是,HashMap 的迭代器(Iterator) 是 fail-fast 迭代器,而 Hashtable 的 enumerator 迭代器不是 fail-fast 的。所以,当有其他线程改变了 HashMap 的结构 (增加或者删除了元素),将会抛出 ConcurrentModificationException异常,但迭代器本身的 remove() 方法移除元素则不会抛出该异常。但这并不是一定会发生的行为,要看 JVM。这也是 Iterator 和 Enumeration 之间的区别。

        (5) HashMap 不能保证随着时间的推移,Map 中的元素次序不发生改变。

备注:

        ① synchronized 意味着一次只有一个线程能够更改 Hashtable。也就是说任何线程要更新 Hashtable 首先要获得同步锁,其他线程要等到同步锁被释放之后才能获得同步锁,对 Hashtable 进行相应的更新;

        ② fail-fast 和 Iterator 迭代器相关。如果某个集合对象创建了 Iterator 或者 ListIterator,然后其他的线程试图“结构上”更改集合对象,将会抛出 ConcurrentModificationException 异常。但其他线程可以通过 set() 方法更改集合对象是允许的,因为这并没有从“结构上”更改集合。但是假如已经从结构上进行了更改,再调用 set() 方法,将会抛出 IIIegalArgumentException 异常。

        ③ 结构上的更改指的是删除或者插入元素,这样会影响到 map 的结构。

        ④ 要使 HashMap 实现同步,可以通过:Map map = Collections.synchronizeMap(hashMap); 来实现。

5. HashMap 和 HashSet 的区别

        (1) HashMap:实现了 Map 接口,Map 接口对键值对进行映射。Map 中不允许重复的键。Map 接口有两个基本的实现,HashMap 和 TreeMap。TreeMap 保存了对象的排列次序,而 HashMap 则不能;HashMap 是非线程安全的,但 collection 框架提供了能够保证 HashMap 线程安全的方法(Collections.synchronizeMap(hashMap)),这样当多个线程同时访问 HashMap 时,能够保证只有一个县城更改 Map。

        (2) HashSet:实现了 Set 接口,它不允许集合中有重复的值,当使用 HashSet 时,需要在将对象存储在 HashSet 之前,确保对象重写 equals() 和 hashCode() 方法,这样才能比较对象的值是否相等,以确保 set 集合中没有存储相等的对象。如果没有重写这两个方法,将会使用这个方法的默认实现进行比较。

        在向 set 集合中添加元素时,使用 public boolean add(Object o) 方法,若元素值重复时就会立即返回 false,若添加成功则会返回true。

        (3) 二者的区别:

 HashMapHashSet
实现接口:实现了 Map 接口实现了 Set接口
存储内容:存储键值对仅仅存储对象
存储方法:存入元素使用:put(key, value)存入元素使用:add(Object o)
hashCode 计算方法:使用键对象来计算 hashCode 值使用成员对象来计算 hashCdoe 值;对于两个对象来说,hashCode 值可能相同,所以 equals() 方法来判断对象的相等性,若两个对象不同的话,返回 false
执行速度:执行速度较快;因为是使用唯一的键来获取对象的执行速度较慢

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值