当前位置:   article > 正文

jdk1.8 hashmap

jdk1.8 hashmap

1、HashMap概述

        在JDK1.8之前,HashMap采用数组+链表实现,即使用链表处理冲突,同一hash值的节点都存储在一个链表里。但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。而JDK1.8中,HashMap采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。

      下图中代表jdk1.8之前的hashmap结构,左边部分即代表哈希表,也称为哈希数组,数组的每个元素都是一个单链表的头节点,链表是用来解决冲突的,如果不同的key映射到了数组的同一位置处,就将其放入单链表中

                jdk1.8之前hashmap结构图

     jdk1.8之前的hashmap都采用上图的结构,都是基于一个数组和多个单链表,hash值冲突的时候,就将对应节点以链表的形式存储。如果在一个链表中查找其中一个节点时,将会花费O(n)的查找时间,会有很大的性能损失。到了jdk1.8,当同一个hash值的节点数不小于8时,不再采用单链表形式存储,而是采用红黑树,如下图所示。

                                                        jdk1.8 hashmap结构图

说明:上图很形象的展示了HashMap的数据结构(数组+链表+红黑树),桶中的结构可能是链表,也可能是红黑树,红黑树的引入是为了提高效率。

2、处理hash冲突的链表和红黑树以及位桶(数据结构)

2.1 链表的实现

Node是HashMap的一个内部类,实现了Map.Entry接口,本质是就是一个映射(键值对)。上图中的每个黑色圆点就是一个Node对象。来看具体代码:

 

 

  1. //Node是单向链表,它实现了Map.Entry接口
  2. static class Node<k,v> implements Map.Entry<k,v> {
  3. final int hash;
  4. final K key;
  5. V value;
  6. Node<k,v> next;
  7. //构造函数Hash值键值下一个节点
  8. Node(int hash, K key, V value, Node<k,v> next) {
  9. this.hash = hash;
  10. this.key = key;
  11. this.value = value;
  12. this.next = next;
  13. }
  14. public final K getKey() { return key; }
  15. public final V getValue() { return value; }
  16. public final String toString() { return key + = + value; }
  17. public final int hashCode() {
  18. return Objects.hashCode(key) ^ Objects.hashCode(value);
  19. }
  20. public final V setValue(V newValue) {
  21. V oldValue = value;
  22. value = newValue;
  23. return oldValue;
  24. }
  25. //判断两个node是否相等,若key和value都相等,返回true。可以与自身比较为true
  26. public final boolean equals(Object o) {
  27. if (o == this)
  28. return true;
  29. if (o instanceof Map.Entry) {
  30. Map.Entry<!--?,?--> e = (Map.Entry<!--?,?-->)o;
  31. if (Objects.equals(key, e.getKey()) &&
  32. Objects.equals(value, e.getValue()))
  33. return true;
  34. }
  35. return false;
  36. }
  37. }

 

 

可以看到,node中包含一个next变量,这个就是链表的关键点,hash结果相同的元素就是通过这个next进行关联的。

2.2 红黑树

 

 

  1. //红黑树
  2. static final class TreeNode<k,v> extends LinkedHashMap.Entry<k,v> {
  3. TreeNode<k,v> parent; // 父节点
  4. TreeNode<k,v> left; //左子树
  5. TreeNode<k,v> right;//右子树
  6. TreeNode<k,v> prev; // needed to unlink next upon deletion
  7. boolean red; //颜色属性
  8. TreeNode(int hash, K key, V val, Node<k,v> next) {
  9. super(hash, key, val, next);
  10. }
  11. //返回当前节点的根节点
  12. final TreeNode<k,v> root() {
  13. for (TreeNode<k,v> r = this, p;;) {
  14. if ((p = r.parent) == null)
  15. return r;
  16. r = p;
  17. }
  18. }
  19. }

 

 

红黑树比链表多了四个变量,parent父节点、left左节点、right右节点、prev上一个同级节点,红黑树内容较多,不在赘述。

2.3 位桶

transient Node<k,v>[] table;//存储(位桶)的数组

HashMap类中有一个非常重要的字段,就是 Node[] table,即哈希桶数组,明显它是一个Node的数组。

     有了以上3个数据结构,只要有一点数据结构基础的人,都可以大致联想到HashMap的实现了。首先有一个每个元素都是链表(可能表述不准确)的数组,当添加一个元素(key-value)时,就首先计算元素key的hash值,以此确定插入数组中的位置,但是可能存在同一hash值的元素已经被放在数组同一位置了,这时就添加到同一hash值的元素的后面,他们在数组的同一位置,但是形成了链表,所以说数组存放的是链表。而当链表长度太长时,链表就转换为红黑树,这样大大提高了查找的效率。

3、HashMap源码分析

3.1 类的继承关系

public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable

     可以看到HashMap继承自父类(AbstractMap),实现了Map、Cloneable、Serializable接口。其中,Map接口定义了一组通用的操作;Cloneable接口则表示可以进行拷贝,在HashMap中,实现的是浅层次拷贝,即对拷贝对象的改变会影响被拷贝的对象;Serializable接口表示HashMap实现了序列化,即可以将HashMap对象保存至本地,之后可

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/564431
推荐阅读
  

闽ICP备14008679号