HashMap 底层实现原理

HashMap 底层实现原理要了解 HashMap 的底层实现原理 我们首先要对 HashMap 的部分底层源码进行分析 publicclassH K V extendsAbstr K V implementsMa K V Cloneable Serializable 我们可以看出 HashMap 继承了 AbstractMap 实现了 Map Cloneable Serializable 接口 staticfinali I K V K V K V

要了解HashMap的底层实现原理,我们首先要对HashMap的部分底层源码进行分析

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

我们可以看出HashMap继承了AbstractMap,实现了Map,Cloneable,Serializable接口。

HashMap 底层实现原理

 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

HashMap的默认初始容量(必须是2的幂),它的值是1左移4位,也就是16

static final int MAXIMUM_CAPACITY = 1 << 30;

HashMap的最大容量,如果任何一个带参数的构造函数指定了较大的值,就会使用它来比较,而且值必须是2的幂,并且小于1左移30位,2的30次方,也就是

 static final float DEFAULT_LOAD_FACTOR = 0.75f;

HashMap默认的加载因子,因为从上可知,HashMap的默认初始容量是16,当HashMap的实际容量达到16*0.75,就是12,HashMap会自动扩容。

 static final int TREEIFY_THRESHOLD = 8;

HashMap底层链表转换成红黑树的临界值,当链表的长度大于8时,会自动转化成红黑树。

static final int UNTREEIFY_THRESHOLD = 6;

HashMap底层红黑树转化成链表的临界值,当红黑树的长度小于6时,红黑树又会自动转换成链表。

static final int MIN_TREEIFY_CAPACITY = 64;

HashMap转化成树结构的最小容量,因为HashMap底层时数组+链表+红黑树实现的,HashMap的容量大于64时,会自动转换成树结构,也就是数组的长度大于64时,也会转换成红黑树。


以下是HashMap的静态内部类

HashMap 底层实现原理

static class Node<K,V> implements Map.Entry<K,V> { final int hash;//若放入的节点key的hash值(若key所在的类未进行重写hash()方法的重写,则为Object的hash()方法) 定义数组索引的位置 final K key;//放入节点的key V value;//放入节点的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; } public final K getKey() { return key; } public final V getValue() { return value; } public final String toString() { return key + "=" + value; } public final int hashCode() { return Objects.hashCode(key) ^ Objects.hashCode(value); } public final V setValue(V newValue) { V oldValue = value; value = newValue; return oldValue; } public final boolean equals(Object o) { if (o == this) return true; if (o instanceof Map.Entry) { // 判断key和value是否都相等 Map.Entry<?,?> e = (Map.Entry<?,?>)o; if (Objects.equals(key, e.getKey()) && Objects.equals(value, e.getValue())) // 如果key和value都相等,就返回true return true; } // 如果key或者value不相等,就返回false return false; } } 

继续往下看

HashMap 底层实现原理

static final int hash(Object key) { // 计算key的hash值 int h; // 1.先拿到key的hashCode值; 2.将hashCode的高16位参与运算 return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); } 
static Class<?> comparableClassFor(Object x) { // 1.判断x是否实现了Comparable接口 if (x instanceof Comparable) { Class<?> c; Type[] ts, as; Type t; ParameterizedType p; // 2.校验x是否为String类型 if ((c = x.getClass()) == String.class) // bypass checks return c; if ((ts = c.getGenericInterfaces()) != null) { // 3.遍历x实现的所有接口 for (int i = 0; i < ts.length; ++i) { // 4.如果x实现了Comparable接口,则返回x的Class if (((t = ts[i]) instanceof ParameterizedType) && ((p = (ParameterizedType)t).getRawType() == Comparable.class) && (as = p.getActualTypeArguments()) != null && as.length == 1 && as[0] == c) // type arg is c return c; } } } return null; }

HashMap 底层实现原理

 / * HashMap的成员变量数组 */ transient Node<K,V>[] table; / * 存储的是entrySet,其实里面的每个元素就是HashMap的每个节点 */ transient Set<Map.Entry<K,V>> entrySet; / * HashMap中已存元素的个数 size */ transient int size; / * HashMap被修改的次数 put remove 操作的次数 */ transient int modCount; //阈值 capacity*loadFactor 或者是将要初始化扩容的容量值 int threshold; / * 负载因子 */ final float loadFactor; 

简单看一下底层源码,我们可以做出以下总结:

HashMap的初始容量是16,默认加载因子是0.75,扩容按照原始容量的2倍扩容,可以存储null值和null键,因为没有加锁,当多个线程同时操作一个hashmap就可能出现不安全的情况,所以线程也是不安全的,底层是由数组+链表+红黑树实现。

当添加一个元素(key-value)时,就首先计算元素key的hash值,以此确定插入数组中的位置,但是可能存在同一hash值的元素,这时就添加到同一hash值的元素的后面,他们在数组的同一位置,但是形成了链表,同一各链表上的Hash值是相同的,所以说数组存放的是链表。而当链表长度大于8时,链表就转换为红黑树,以及当数组的长度大于64时,也会自动转换成红黑树,这样大大提高了查找的效率。

通过哈希表函数/哈希算法,将hash值转换成数组的下标,下标位置上如果没有任何元素,就把Node(节点)添加到这个位置上。如果说下标对应的位置上有链表。此时,就会拿着key和链表上每个节点的key进行equals。如果所有的equals方法返回都是false,那么这个新的节点将被添加到链表的末尾。如其中有一个equals的结果返回了true,那么这个节点的value将会被覆盖。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/233677.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • PID控制算法总结

    PID控制算法总结当今的闭环自动控制技术都是基于反馈的概念以减少不确定性。反馈理论的要素包括三个部分:测量、比较和执行。测量关键的是被控变量的实际值,与期望值相比较,用这个偏差来纠正系统的响应,执行调节控制。在工程实际中,应用最为广泛的调节器控制规律为比例、积分、微分控制,简称PID控制,又称PID调节。一、PID含义PID是英文单词比例(Proportion),积分(Integral),微分(Di…

    2022年5月12日
    73
  • 双系统Ubuntu分区(双系统ubuntu100g分区方案)

    假设整个空闲空间有200G,主要分4个区:1.给系统分区EFI:在唯一的一个空闲分区上添加,大小200M,逻辑分区,空间起始位置,用于efi;这个分区必不可少,用于安装ubuntu启动项。(注意与Windows系统中的EFI区分开,)2.swap分区:中文是”交换空间”,充当ubuntu的虚拟内存,一般的大小为电脑物理内存的2倍左右,选中空闲磁盘,点击+,选择逻辑分区、“空间起始位置”,用于后面选择“交换空间”,给它分区16g空间(举例),然后点击确定。3./:这是ubuntu的根目录,

    2022年4月14日
    530
  • strip 命令的使用方法

    strip 命令的使用方法

    2021年12月8日
    45
  • 作为大学生,如何通过学校认证免费获取正版matlab[通俗易懂]

    作为大学生,如何通过学校认证免费获取正版matlab[通俗易懂]时间有限,内容从简主要介绍大学生如何免费获取正版matlab,前提是贵学校已经为你提供了正版的matlab!!!否则,可以直接点右上角了。1.背景介绍作为前大学生,需要用matlab,又不想用盗版,也不想搞激活成功教程之类的,麻烦死了1.1直接上MathWorks官网matlab正版对于学生来说很贵。standard版的一年¥6,200,永久的¥15,500.education版的分…

    2022年10月11日
    0
  • Map集合的遍历[通俗易懂]

    Map集合的遍历[通俗易懂]COPY/***Map接口的使用*特点:1.存储键值对2.键不能重复,值可以重复3.无序*/publicclassDemo1{ publicstaticvoidmain(String[]args){ Map<String,Integer>map=newHashMap<String,Integer>(); //1.添加元素 map.put(“tang”,21); map.put(“he”,22); map.put(“

    2022年5月7日
    38
  • Tomcat学习—Tomcat的web.xml配置文件「建议收藏」

    今天开始学习Tomcat的配置文件,自己学习和上网查看整理web.xml 的笔记!

    2022年2月24日
    57

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号