1 HashMap

1)特性:

底层数据结构是数组+链表+红黑树运行null键和null值,,非线程安全,不保证有序,插入和读取顺序不保证一致,不保证有序,在扩容时,元素的顺序会被重新打乱
实现原理:
Hashmap先采用算法将key散列为一个int值,这个int值对应到数组的下标,如果散列值相同,则在该下标后连接链表,如果链表长度超过8,则构造红黑树,

2)基本属性:

//默认初始化大小
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16、、就是2的四次方
static final int MAXIMUM_CAPACITY = 1 << 30;//最大容器
static final float DEFAULT_LOAD_FACTOR = 0.75f;//默认加载因子
static final int TREEIFY_THRESHOLD = 8;//这个值是从链表变成红黑树的阀门,如果大于这个值就会转变
static final int UNTREEIFY_THRESHOLD = 6;// 红黑树节点小于6的时候转链表
static final int MIN_TREEIFY_CAPACITY = 64;//转换为红黑树之前还得判断数组的容量是否大于64transient Node<K,V>[] table;//存放数据的·1底层数组transient Set<Map.Entry<K,V>> entrySet;//通过entrySet变量,提供遍历的功能transient int size;//数组table的实际大小transient int modCount;//操作次数int threshold;//判断是否需要调整HashMap,如果table数组还没被分配时,阈值threshold等于数组的数组容量,反之threshold = capacity * load factorfinal float loadFactor;//负载因子

Node的属性

static class Node<K,V> implements Map.Entry<K,V> {final int hash;final K key;V value;Node<K,V> next;Node(int hash, K key, V value, Node<K,V> next) {this.hash = hash;this.key = key;this.value = value;this.next = next;}}

(1)hash:表示key的hash
(2)key:表示我们给出的key,就是map(key,value)中的key
(3)value:表示我们给出的value,就是map(key,value)中的value
(4)next:如果hash相同,会形成链表,当前链表节点的下一个链表的引用,即链表后继
如果链表长度大于8,为了查询性能,会把链表转为红黑树(TreeNode)

 static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {TreeNode<K,V> parent;  // red-black tree linksTreeNode<K,V> left;TreeNode<K,V> right;TreeNode<K,V> prev;    // needed to unlink next upon deletionboolean red;TreeNode(int hash, K key, V val, Node<K,V> next) {super(hash, key, val, next);}}

(1)parent:表示节点的父节点
(2)left:表示左节点
(3)right:表示右节点
(4)prev:表示前驱节点
(5)red:表示是红树还是黑树

3)构造方法

public HashMap() {this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted}
public HashMap(int initialCapacity) {this(initialCapacity, DEFAULT_LOAD_FACTOR);}
public HashMap(int initialCapacity, float loadFactor) {if (initialCapacity < 0)throw new IllegalArgumentException("Illegal initial capacity: " +initialCapacity);if (initialCapacity > MAXIMUM_CAPACITY)initialCapacity = MAXIMUM_CAPACITY;if (loadFactor <= 0 || Float.isNaN(loadFactor))//小于0或者为空throw new IllegalArgumentException("Illegal load factor: " +loadFactor);this.loadFactor = loadFactor;//负载因子 //返回2的n次方 》=initialCapacitythis.threshold = tableSizeFor(initialCapacity);//}
public HashMap(Map<? extends K, ? extends V> m) {this.loadFactor = DEFAULT_LOAD_FACTOR;putMapEntries(m, false);}

4)核心方法

1 put方法

public V put(K key, V value) {return putVal(hash(key), key, value, false, true);
}
//获取key的hash值
static final int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); //注意当key为null是会返回0,hashmap允许key为null进行存储,且存在table[0]的位置。另外会对获取的hashcode进行高低16位按位与,减小hash冲突的概率
}final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {Node<K,V>[] tab; Node<K,V> p; int n, i;if ((tab = table) == null || (n = tab.length) == 0)//table是底层数组,判断table是否为空或者nulln = (tab = resize()).length;// 成立用resize()扩容if ((p = tab[i = (n - 1) & hash]) == null)//计算插入的元素在hash桶中的位置,若该位置还没有元素tab[i] = newNode(hash, key, value, null);//插入else {Node<K,V> e; K k;if (p.hash == hash &&//判断该位置的第一个元素是否与我们要插入的元素相同((k = p.key) == key || (key != null && key.equals(k))))e = p;//如果相同则记录该节点到变量eelse if (p instanceof TreeNode)//否则判断第一个节点是否为树类型的节点e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);//如果是,则调用树类型的插入操作else {//否则,第一个元素既不与我们要插入的节点相同,又不是树类型的节点,那么去遍历链表for (int binCount = 0; ; ++binCount) {if ((e = p.next) == null) {//若节点为空则表示已经遍历到链表的最后,此时新建一个节点并加入到末尾p.next = newNode(hash, key, value, null);if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st//判断链表的长度是否大于8treeifyBin(tab, hash); //将链表转为红黑树break;}if (e.hash == hash &&//如果某个节点与我们即将插入的节点相同,则跳出循环((k = e.key) == key || (key != null && key.equals(k))))break;p = e;}}if (e != null) { // existing mapping for key//此时e节点中记录的是hashMap中与我们要插入的节点相同的节点,在这里统一进行处理V oldValue = e.value;//记录旧的value值if (!onlyIfAbsent || oldValue == null)//通过onlyIfAbsent与oldValue的值判断是否要进行覆盖e.value = value;afterNodeAccess(e);return oldValue;//返回旧的值}}++modCount;//代表hashMap的结构被改变if (++size > threshold)//判断是否要进行扩容resize();afterNodeInsertion(evict);return null;}

扩容函数

final Node<K,V>[] resize() {Node<K,V>[] oldTab = table;//记录原来的tableint oldCap = (oldTab == null) ? 0 : oldTab.length;//判断原来的table是否为空,若为空则oldCap = 0,否则oldCap = oldTab.lengthint oldThr = threshold;//记录原来的阙值int newCap, newThr = 0;//创建变量用来记录新的容量和阙值if (oldCap > 0) {//判断原来的容量是否大于0,由于HashMap是在第一次put是才会进行初始化,因此此方法也是判断table是要扩容还是要初始化.大于0代表已经初始化过了if (oldCap >= MAXIMUM_CAPACITY) {//如果原来的容量大于0且大于等于最大值threshold = Integer.MAX_VALUE;//将阙值设为最大值,并返回原来的容量,代表该table已经不能再进行扩容return oldTab;}//原来的容量乘以2else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&oldCap >= DEFAULT_INITIAL_CAPACITY)newThr = oldThr << 1; // double threshold//新的界限为原来界限的一倍}//如果说oldCap为0(代表hashMap没有被初始化过)且原来的阙值大于0else if (oldThr > 0) // initial capacity was placed in thresholdnewCap = oldThr;//否则说明我们在新建hashMap是没有指定初始值或是我们将初始大小设为了0else {               // zero initial threshold signifies using defaultsnewCap = DEFAULT_INITIAL_CAPACITY;//设为默认值16newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);//阙值设为16*0.75}if (newThr == 0) {float ft = (float)newCap * loadFactor;newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?(int)ft : Integer.MAX_VALUE);}threshold = newThr;//设置当前的阙值为新的阙值@SuppressWarnings({"rawtypes","unchecked"})Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];//以新的容量去新建一个table数组table = newTab;if (oldTab != null) {//若原来的table是否为空,代表现在是要进行扩容操作for (int j = 0; j < oldCap; ++j) {//遍历hash桶Node<K,V> e;if ((e = oldTab[j]) != null) {//遍历每一个hash桶中的元素,并记录第一个节点到变量eoldTab[j] = null;//将原来的位置设为nullif (e.next == null)//如果只有一个元素newTab[e.hash & (newCap - 1)] = e;//计算新的位置,并插入else if (e instanceof TreeNode)//如果是树节点,则转为红黑树的扩容操作((TreeNode<K,V>)e).split(this, newTab, j, oldCap);else { // preserve orderNode<K,V> loHead = null, loTail = null;//将每一个桶中的元素分为两类,扩容后的位置与原来相同则记录到loHead,loTail这个链表中,扩容后与原来的位置不同则记录到hiHead,hiTail中Node<K,V> hiHead = null, hiTail = null;Node<K,V> next;do {next = e.next;if ((e.hash & oldCap) == 0) {if (loTail == null)loHead = e;elseloTail.next = e;loTail = e;}else {if (hiTail == null)hiHead = e;elsehiTail.next = e;hiTail = e;}} while ((e = next) != null);if (loTail != null) {loTail.next = null;newTab[j] = loHead;//将loHead链表写入到新的table}if (hiTail != null) {//将hiHead链表记录到新的tablehiTail.next = null;newTab[j + oldCap] = hiHead;}}}}}return newTab;}

删除方法

 public V remove(Object key) {Node<K,V> e;return (e = removeNode(hash(key), key, null, false, true)) == null ?null : e.value;}
 final Node<K,V> removeNode(int hash, Object key, Object value,boolean matchValue, boolean movable) {Node<K,V>[] tab; Node<K,V> p; int n, index;//找到要删除的元素存在p所在的下标if ((tab = table) != null && (n = tab.length) > 0 &&(p = tab[index = (n - 1) & hash]) != null) {Node<K,V> node = null, e; K k; V v;//hash没有冲突if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))//定位删除的节点node = p;//不止是一个节点在该位置上else if ((e = p.next) != null) {if (p instanceof TreeNode)node = ((TreeNode<K,V>)p).getTreeNode(hash, key);else {do { //遍历链表if (e.hash == hash &&((k = e.key) == key ||(key != null && key.equals(k)))) {node = e;break;}p = e;} while ((e = e.next) != null);}}//node要删除的元素if (node != null && (!matchValue || (v = node.value) == value ||(value != null && value.equals(v)))) {if (node instanceof TreeNode)((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);else if (node == p)tab[index] = node.next;else//p的下一个元素等于要删除的元素的下一个p.next = node.next;++modCount;--size;afterNodeRemoval(node);return node;}}return null;}

使用迭代器对数组进行遍历,中间不能对map进行操作

final Node<K,V> nextNode() {Node<K,V>[] t;Node<K,V> e = next;//修改次数不等于最后一次修改次数的话就会报错if (modCount != expectedModCount)throw new ConcurrentModificationException();if (e == null)throw new NoSuchElementException();if ((next = (current = e).next) == null && (t = table) != null) {do {} while (index < t.length && (next = t[index++]) == null);//寻找数组中不为空的节点进行遍历}return e;}

Java集合系列---HashMap源码解析(超详细)相关推荐

  1. Java集合系列---List源码解析(ArrayList和LinkedList的区别)

    List源码主要讲ArrayList,LinkedList,Vector三个类 1 ArrayList ArrayList是一个底层基于数组的集合, 首先来看一下它的继承关系, public clas ...

  2. Java集合系列---Collection源码解析及整体框架结构

    集合的整体框架结构及依赖关系 1.Collection public interface Collection<E> extends Iterable<E> {} Collec ...

  3. Java集合系列---ConcurrentHashMap源码解析

    ConcurrentHashMap是Java并发容器的一员,jdk1.8以后的基本的数据结构和HashMap相似,也是选用了数组+链表/红黑树的结构,在jdk1,.7以前则是采用了分段锁的技术.Con ...

  4. Java集合系列---LinkedHashMap源码解析

    1 首先来看一下LinkedHashMap的继承关系 public class LinkedHashMap<K,V> extends HashMap<K,V> implemen ...

  5. Java集合系列---TreeMap源码解析(巨好懂!!!)

    TreeMap底层是基于红黑树实现,能实现根据key值对节点进行排序,排序可分为自然排序和定制排序. 自然排序:TreeMap的所有key必须实现Comparable接口, 定制排序:创建TreeMa ...

  6. Java集合之TreeMap源码解析上篇

    上期回顾 上期我从树型结构谈到了红黑树的概念以及自平衡的各种变化(指路上期←戳),本期我将会对TreeMap结合红黑树理论进行解读. 首先,我们先来回忆一下红黑树的5条基本规则. 1.结点是红色或者黑 ...

  7. hashmap删除指定key_Java集合之HashMap源码解析(JDK8)

    哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景非常丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,而HashMap的实现原理也常常出 ...

  8. Java集合:HashMap源码剖析

    一.HashMap概述 二.HashMap的数据结构 三.HashMap源码分析      1.关键属性      2.构造方法      3.存储数据      4.调整大小 5.数据读取     ...

  9. hashmap修改对应key的值_死磕 java集合之HashMap源码分析

    简介 HashMap采用key/value存储结构,每个key对应唯一的value,查询和修改的速度都很快,能达到O(1)的平均时间复杂度.它是非线程安全的,且不保证元素存储的顺序: 继承体系 Has ...

最新文章

  1. BCH压力测试即将开始,你确定不来凑凑热闹?
  2. Dependency Walker PE模块依赖性分析工具
  3. JavaSE(十七)——IO流之字节流
  4. Python爬虫之旅_高性能异步爬虫
  5. Hypersonic SQL开源数据库方向比较流行的纯Java开发的关系型数据库
  6. 某银行大型管理系统端到端持续集成和交付实践
  7. 在.net中序列化读写xml方法的总结(转载)
  8. js如何保证iframe里的内容,显示在父窗口
  9. 系统之美——系统思考与认识系统
  10. Java高并发-多线程基础
  11. 2-2-2-webpack打包
  12. SAP案例教程CO成本会计后台配置
  13. android获取连接wifi名称,Android开发:获取手机当前连接的WiFi名称
  14. 【量化交易】 python 基本语法与变量 【003】 策略 复习一下
  15. 新浪微博应用开发入门
  16. 多小波分解2-D图像
  17. 三维家隐藏的如何再出现_三维建模基础知识
  18. Hbase in action部分章节阅读笔记
  19. 养老江湖:十年十败,一部跌宕起伏的中国养老史诗
  20. win10我的电脑图标怎么放在桌面,win10我的电脑图标放在桌面的方法

热门文章

  1. 基本配置4-被忽悠进了CentOS 6
  2. android 拨打电话与发送短信
  3. html body不定宽居中,纯CSS实现元素垂直水平居中-非固定宽度
  4. config修改php背景颜色,动态永久修改config
  5. python大纲_python学习大纲
  6. 一些js/css动画 mark
  7. preg_match进行正则表达式匹配
  8. U盘文件系统类型 和 linux 挂载 和 卸载
  9. servlet运行原理和生命周期
  10. Go如何对数组切片进行去重