HashMap原理分析
来源:互联网 发布:股市模拟交易软件下载 编辑:程序博客网 时间:2024/05/17 07:35
参考文章:
1.深入解析hashcode,hashMap源码
2.【Java集合源码剖析】HashMap源码剖析
3.Java 8系列之重新认识HashMap
HashMap概述
HashMap是基于哈希表实现的,每一个元素是一个key-value对,其内部通过单链表(JDK1.8中长度大于8就将链表改成了二叉树)解决冲突问题,容量不足(超过了阀值)时会自动增长。
HashMap是非线程安全的,只是用于单线程环境下,多线程环境下可以采用concurrent并发包下的ConcurrentHashMap。
HashMap的数据结构
如图所示,HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。首先,HashMap类的属性中定义了Entry(JDK1.8中是Node)类型的数组。Entry类实现java.ultil.Map.Entry接口,同时每一对key和value是作为Entry类的属性被包装在Entry的类中。
transient Entry[] table; static class Entry<K,V> implements Map.Entry<K,V> { final K key; V value; Entry<K,V> next; final int hash; …… }
可以看出,HashMap底层就是一个数组结构,数组中的每一项又是一个链表。当新建一个HashMap的时候,就会初始化一个数组。table数组的元素是Entry类型的。每个 Entry元素其实就是一个key-value对,并且它持有一个指向下一个 Entry元素的引用,这就说明table数组的每个Entry元素同时也作为某个Entry链表的首节点,指向了该链表的下一个Entry元素,这就是所谓的“链表散列”数据结构,即数组和链表的结合体。
HashMap的几个主要方法
构造方法:
// 指定容量大小和加载因子的构造函数 public HashMap(int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity); // HashMap的最大容量只能是MAXIMUM_CAPACITY if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; //加载因此不能小于0 if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal load factor: " + loadFactor); // 找出大于initialCapacity的最小的2的幂 int capacity = 1; while (capacity < initialCapacity) capacity <<= 1; // 设置加载因子 this.loadFactor = loadFactor; // 设置HashMap阈值,当HashMap中存储数据的数量达到threshold时,就需要将HashMap的容量加倍。 threshold = (int)(capacity * loadFactor); // 创建Entry数组,用来保存数据 table = new Entry[capacity]; init(); }
put():
public V put(K key, V value) { // HashMap允许存放null键和null值。 // 当key为null时,调用putForNullKey方法,将value放置在数组第一个位置。 if (key == null) return putForNullKey(value); // 当key不为null,计算key的hash值。 int hash = hash(key.hashCode()); // 搜索指定hash值在对应table中的索引。 int i = indexFor(hash, table.length); // 如果 i 索引处的 Entry 不为 null,通过循环不断遍历 e 元素的下一个元素。 for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; // 如果发现 i 索引处的链表的某个Entry的hash和新Entry的hash相等且两者的key相同,则新Entry覆盖旧Entry,返回。 if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } // 如果i索引处的Entry为null,表明此处还没有Entry。 modCount++; // 将key、value添加到i索引处。 addEntry(hash, key, value, i); return null; }
put()流程图(基于JDK1.8):
①.判断键值对数组table[i]是否为空或为null,否则执行resize()进行扩容;
②.根据键值key计算hash值得到插入的数组索引i,如果table[i]==null,直接新建节点添加,转向⑥,如果table[i]不为空,转向③;
③.判断table[i]的首个元素是否和key一样,如果相同直接覆盖value,否则转向④,这里的相同指的是hashCode以及equals;
④.判断table[i] 是否为treeNode,即table[i] 是否是红黑树,如果是红黑树,则直接在树中插入键值对,否则转向⑤;
⑤.遍历table[i],判断链表长度是否大于8,大于8的话把链表转换为红黑树,在红黑树中执行插入操作,否则进行链表的插入操作;遍历过程中若发现key已经存在直接覆盖value即可;
⑥.插入成功后,判断实际存在的键值对数量size是否超多了最大容量threshold,如果超过,进行扩容。
红黑树的结构是JDK1.8之后加入的。
get():
public V get(Object key) { if (key == null) return getForNullKey(); // 获取key的hash值 int hash = hash(key.hashCode()); // 在该hash值对应的链表上查找键值等于key的元素 for (Entry<K,V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) { Object k; //判断key是否相同 if (e.hash == hash && ((k = e.key) == key || key.equals(k))) return e.value; } //没找到则返回null return null; }
resize():
void resize(int newCapacity) { Entry[] oldTable = table; int oldCapacity = oldTable.length; if (oldCapacity == MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return; } // 新建一个HashMap,newCapacity是调整后的初始容量 Entry[] newTable = new Entry[newCapacity]; // 将旧的HashMap的全部元素添加到新的HashMap中 transfer(newTable); table = newTable; threshold = (int)(newCapacity * loadFactor); }
- HASHMAP的原理分析
- hashMap原理分析
- HashMap实现原理分析
- HashMap实现原理分析
- HashMap实现原理分析
- HashMap实现原理分析
- HashMap实现原理分析
- HashMap实现原理分析
- HashMap实现原理分析
- HashMap实现原理分析
- HashMap实现原理分析
- HashMap实现原理分析
- HashMap实现原理分析
- HashMap实现原理分析
- HashMap实现原理分析
- HashMap实现原理分析
- HashMap实现原理分析
- HashMap原理分析
- 设计模式之观察者模式
- POJ 1160 Post Office (区间DP+重心)
- redis的数据类型
- python创建Django项目Couldn't import Django解决思路
- Trafodion表物理设计与逻辑设计问答
- HashMap原理分析
- python3小项目——爬取招聘信息(智联招聘)
- Android studio http请求获取数据失败或者获取不到数据原因
- 21 用户进程操作GPIO
- H5+JS+CSS3 实现圣诞情缘--学习心得2
- Java——方法的可变参数
- JDBC操作步骤
- sql 去重 (DISTINCT)详解
- Ffmpeg源代码简单分析之解码2