iis服务器助手广告广告
返回顶部
首页 > 资讯 > 精选 >如何用源码分析Java HashMap实例
  • 255
分享到

如何用源码分析Java HashMap实例

2023-06-17 06:06:21 255人浏览 独家记忆
摘要

本篇文章为大家展示了如何用源码分析Java HashMap实例,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。引言HashMap在键值对存储中被经常使用,那么它到底是如何实现键值存储的呢?一 Entr

本篇文章为大家展示了如何用源码分析Java HashMap实例,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。

引言

HashMap在键值对存储中被经常使用,那么它到底是如何实现键值存储的呢?

一 Entry

Entry是Map接口中的一个内部接口,它是实现键值对存储关键。在HashMap中,有Entry的实现类,叫做Entry。Entry类很简 单,里面包含key,value,由外部引入的hash,还有指向下一个Entry对象的引用,和数据结构中学的链表中的note节点很类似。

Entry类的属性和构造函数:

final K key; V value; Entry<K,V> next; int hash;  Entry(int h, K k, V v, Entry<K,V> n) { value = v; next = n; key = k; hash = h; }

二 HashMap的初始化

//HashMap构造方法 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))   throw new IllegalArgumentException("Illegal load factor: " +              loadFactor); this.loadFactor = loadFactor; threshold = initialCapacity; init(); }

这是HashMap的构造函数之一,其他构造函数都引用这个构造函数进行初始化。参数InitialCapacity指的是HashMap中 table数组最初的大小,参数loadFactory指的是HashMap可容纳键值对与数组长度的比值(举个例子:数组长度默认值为 16,loadFactory默认值为0.75,如果HashMap中存储的键值对即Entry多于12,则会进行扩容,扩容后大小为当前数组长度的2 倍)。在构造函数中不会对数组进行初始化,只有在put等操作方法内会进行判断是否要初始化或扩容。

三 table数组

在HashMap中有一个概念叫做threshold(实际可容纳量),实际可容纳量指的是在HashMap中允许存在最多的Entry的个数,它 是由HashMap中内置的数组table的长度*load factory(负载因子)得来。其作用是保证HashMap的效率。

table数组是HashMap实现键值对存储的又一关键,具体键值对是怎么存的呢?请看下图

如何用源码分析Java HashMap实例

如图中的[key,value]就是Entry对象来实现的,而table数组是用来存放Entry对象的。

//数组的初始化:

private static int roundUpToPowerOf2(int number) { return number >= MAXIMUM_CAPACITY    ? MAXIMUM_CAPACITY    : (number > 1) ? Integer.highestOneBit((number - 1) << 1) : 1; } private void inflateTable(int toSize) { // Find a power of 2 >= toSize int capacity = roundUpToPowerOf2(toSize); threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1); table = new Entry[capacity]; initHashSeedAsNeeded(capacity); }

在put等方法中发现数组未进行初始化时会调用InflateTable方法进行初始化,输入参数为初始设置的InitialCapacity,实 际上他会调用roundUpToPowerOf2方法返回一个比初始容量大的最小的2的幂数(其中一个原因是在得到Entry所在数组位置时方便)。

四 put方法

public V put(K key, V value) { if (table == EMPTY_TABLE) {   inflateTable(threshold); } if (key == null)   return putForNullKey(value); int hash = hash(key); int i = indexFor(hash, table.length); for (Entry<K,V> e = table[i]; e != null; e = e.next) {   Object k;   if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {    V oldValue = e.value;    e.value = value;    e.recordAccess(this);    return oldValue;   } } modCount++; addEntry(hash, key, value, i); return null; } private V putForNullKey(V value) { for (Entry<K,V> e = table[0]; e != null; e = e.next) {   if (e.key == null) {    V oldValue = e.value;    e.value = value;    e.recordAccess(this);    return oldValue;   } } modCount++; addEntry(0, null, value, 0); return null; } void addEntry(int hash, K key, V value, int bucketIndex) { if ((size >= threshold) && (null != table[bucketIndex])) {   resize(2 * table.length);   hash = (null != key) ? hash(key) : 0;   bucketIndex = indexFor(hash, table.length); } createEntry(hash, key, value, bucketIndex); } void createEntry(int hash, K key, V value, int bucketIndex) { Entry<K,V> e = table[bucketIndex]; table[bucketIndex] = new Entry<>(hash, key, value, e); size++; }

在put方法中

首先会判断数组是否为空,如果为空会对数组进行初始化。

接下来判断key是否为null,如果为null就采用第二个方法对键值对进行put。

接下来对key进行hash得到一个数值,再对这个数值进行处理(IndexFor方法)得到所在数组中的位置。

接下来会遍历所在数组位置的链表,如果key的hash和传入key的hash相同且(key内存地址相等 或 equals方法相等),则意味着会更新在链表中的value值,并返回旧的value值。

如果上边的方法都没有奏效,则会调用第三个方法,创建一个新的Entry对象。

在putForNullKey方法中 ,我们看到它是为了NULL值专门设置的,NULL值的hash始终为0,所以key为NULL的Entry对象肯定在数组的第0个位置。同样,如果找到则更新,没有找到则添加。

调用addEntry方法  意味着要往这个数组链表中添加一个Entry,所以会在最开始判断已经存在的Entry数量是否超过了实际可容纳量。如果超过了,则会调用resize方 法将数组扩大两倍,注意在扩大之后会对已经存入的Entry进行重排,原因是当初存入时IndexFor方法与数组长度有关系。接着会调用第四个方法。

createEntry方法 很简单,就是将原本在数组中存放的链表头置入到新的Entry之后,将新的Entry放入数组中。从这里我们可以看出HashMap不保证顺序问题。

get方法和contains方法原理和put方法一致,即先通过对key的hash得到其value值所在的链表头在数组中的位置,再通过equals方法判断value是否存在。

五 其他

//hash方法 final int hash(Object k) { int h = hashSeed; if (0 != h && k instanceof String) {   return sun.misc.Hashing.stringHash42((String) k); } h ^= k.hashCode(); // This function ensures that hashCodes that differ only by // constant multiples at each bit position have a bounded // number of collisions (approximately 8 at default load factor). h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); }

hash方法中最终返回值与key的hashCode方法有关。

总结

  1. 最终数组初始化的容量大小会是大于等于你传入初始容量的最小2的幂数。

  2. key为null或value为null能存入HashMap的原因是对null值会进行单独的操作。

  3. 在table数组中的链表中每个Entry的共同点是key的hash(key.hashCode)部分相同。

  4. 注意对key的hashCode和equals方法的重写当你想让两个key映射一个对象,因为判定key相等的条件是(hashCode相等+(内存相等 或 equals相等))。

  5. 最早存入的键值对会在链表的末端。

  6. 当数组没有链表存在时,HashMap性能***为O(1)。而最差为O(threshould)。

上述内容就是如何用源码分析Java HashMap实例,你们学到知识或技能了吗?如果还想学到更多技能或者丰富自己的知识储备,欢迎关注编程网精选频道。

--结束END--

本文标题: 如何用源码分析Java HashMap实例

本文链接: https://www.lsjlt.com/news/287434.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 如何用源码分析Java HashMap实例
    本篇文章为大家展示了如何用源码分析Java HashMap实例,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。引言HashMap在键值对存储中被经常使用,那么它到底是如何实现键值存储的呢?一 Entr...
    99+
    2023-06-17
  • Java HashMap使用源码分析
    这篇文章主要讲解了“Java HashMap使用源码分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java HashMap使用源码分析”吧!0. 成员变量首先我们先看...
    99+
    2023-07-06
  • Java源码角度分析HashMap用法
    —HashMap—优点:超级快速的查询速度,时间复杂度可以达到O(1)的数据结构非HashMap莫属。动态的可变长存储数据(相对于数组而言)。缺点:需要额外计算一次hash值,如果处理不当会占用额外的空间。—HashMap如何使用—平时我们...
    99+
    2023-05-30
  • Java详解HashMap实现原理和源码分析
    目录学习要点:1、什么是HashMap?2、HashMap的特性3、HashMap的数据结构4、HashMap初始化操作4.1、成员变量4.2、 构造方法5、Jdk8中HashMap...
    99+
    2022-11-12
  • Java数据结构之HashMap和HashSet源码分析
    本篇内容介绍了“Java数据结构之HashMap和HashSet源码分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1、认识 HashMa...
    99+
    2023-07-05
  • Java数据结构之HashMap源码深入分析
    目录基本结构get方法put方法HashMap的容量为什么总是2的n次幂HashMap是Java集合框架中常用的一种数据结构,它是一种基于哈希表实现的映射表.在JDK1.8版本中,H...
    99+
    2023-05-17
    Java HashMap原理 Java HashMap Java HashMap源码
  • 如何进行HashMap扩容机制源码分析
    这期内容当中小编将会给大家带来有关如何进行HashMap扩容机制源码分析,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。具体看源码之前,我们先简单的说一下HashMap的底层数据结构  1、HashMap底...
    99+
    2023-06-02
  • java锁机制ReentrantLock源码实例分析
    目录一:简述二:ReentrantLock类图三:流程简图四:源码分析lock()源码分析:非公平实现:公平锁实现:tryAcquire()方法公平锁实现:非公平锁实现:addWai...
    99+
    2022-11-13
  • Java源码ConcurrentHashMap的示例分析
    小编给大家分享一下Java源码ConcurrentHashMap的示例分析,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!一、记录形式打算直接把过程写在源码中,会按序进行注释,查阅的时候可以按序号只看注释部分二、Concur...
    99+
    2023-06-15
  • 如何解读Java HashMap核心源码
    这期内容当中小编将会给大家带来有关如何解读Java HashMap核心源码,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。对HashMap实现的源码进行简单的分析。 所使用的HashMap源码的版本信息如下...
    99+
    2023-06-17
  • 不容错过的HashMap实现原理及源码分析
    哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,而HashMap的实现原理也常常出现在各类的面试题中,重要性可见一斑。本文...
    99+
    2023-06-02
  • java中CopyOnWriteArrayList源码的示例分析
    这篇文章将为大家详细讲解有关java中CopyOnWriteArrayList源码的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。简介CopyOnWriteArrayList是ArrayList的...
    99+
    2023-06-29
  • Java中Handler源码的示例分析
    这篇文章主要介绍了Java中Handler源码的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。从很早开始就认识到 Handler 了,只不过那时修为尚浅,了解的不够深...
    99+
    2023-06-02
  • Java源码解析之ConcurrentHashMap的示例分析
    小编给大家分享一下Java源码解析之ConcurrentHashMap的示例分析,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!早期 ConcurrentHashMap,其实现是基于:分离锁,也就是将内部进行分段(Segme...
    99+
    2023-06-15
  • java LRU算法及Apache LRUMap源码实例分析
    本篇内容主要讲解“java LRU算法及Apache LRUMap源码实例分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“java LRU算法及Apache LRUMap源...
    99+
    2023-06-21
  • Vue3 源码分析reactive readonly实例
    目录引言一、reactive 和 readonly1. reactive相关类型2. 相关全局变量与方法3. reactive函数4. 造物主createReactiveObject...
    99+
    2022-11-13
    Vue3 reactive readonly Vue3 reactive
  • Java SpringBoot核心源码的示例分析
    本篇文章给大家分享的是有关Java SpringBoot核心源码的示例分析,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。SpringBoot源码主线分析我们要分析一个...
    99+
    2023-06-22
  • 如何进行Netlink源码及实例的分析
    本篇文章给大家分享的是有关如何进行Netlink源码及实例的分析,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。前言这几天在看 ipvs 相关代码的时候又遇到了 netlink ...
    99+
    2023-06-15
  • java代码实例分析
    这篇文章主要介绍“java代码实例分析”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“java代码实例分析”文章能帮助大家解决问题。一、几个坑爹代码的目录这样使用 StringBuffer 的方法有什...
    99+
    2023-06-16
  • JAVA 枚举单例模式及源码分析的实例详解
    JAVA 枚举单例模式及源码分析的实例详解      单例模式的实现有很多种,网上也分析了如今实现单利模式最好用枚举,好处不外乎三点:线程安全不会因为序列化而产生新实例防止反射攻击但是貌似没...
    99+
    2023-05-31
    java 枚举 单例
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作