hash表是一种常见的数据结构,主要是通过hash算法将数据尽可能的散列开来存放,当要查找某一数据时,可以通过hash算法直接定位,节省了对比查找的时间。map是一种key、value形式的键值对,将hash表和map结合即形成了HashMap。
在Java中HashMap的数据是以Entry<key,value>数组的形式存放的,HashMap通过对key进行hash运算得到一个数组下标,然后将数据存放到Entry<key,value>数组对应的位置,又因为不同的key进行hash运算可能会得到一个相同的数组下标,为了解决碰撞覆盖冲突,所以Entry<key,value>本身又是一个链表的结构,即以后不同的key相同数组下标的数据的next会被赋值为已存在Entry<key,value>链表,新的Entry会替换数组值。
HashMap的存储数据的示例图如下:
HashMap 的put方法的源码解析
public V put(K key, V value) { if (key == null) return putForNullKey(value); // HashMap接收key为null的数据 int hash = hash(key.hashCode()); //对key的hashCode再进行hash运算 int i = indexFor(hash, table.length);//根据hash值和entry数组的大小计算出新增数据应该存放的数组位置 for (Entry<K,V> e = table[i]; e != null; e = e.next) { // for循环遍历找到的数组下标的entry,如果hash值和key都相等,则覆盖原来的value值 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++; //如果上面for循环没有找到相同的hash和key,则增加一个entry addEntry(hash, key, value, i); return null; }
void addEntry(int hash, K key, V value, int bucketIndex) { Entry<K,V> e = table[bucketIndex]; //找到下标的entry //new 一个新的entry,赋值给当前下标数组 table[bucketIndex] = new Entry<K,V>(hash, key, value, e); if (size++ >= threshold) resize(2 * table.length); }
Entry(int h, K k, V v, Entry<K,V> n) { value = v; next = n; //即将原来数组下标对应的entry赋值给新的entry的next key = k; hash = h; }
(1)hash值相同且key相等数据将被覆盖。
(2)添加新的entry时,将已存在的数据下标的entry(可能是null)赋值给新entry的next,新entry将替换原数组下标的值。
HashMap的get方法源码解析
public V get(Object key) { //key为null时特别处理 if (key == null) return getForNullKey(); int hash = hash(key.hashCode()); //indexFor(hash, table.length) 根据hash值和数组长度计算出下标,然后遍历Entry链表 for (Entry<K,V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) return e.value; } return null; }
总结
- 一个对象当HashMap的key时,必须覆盖hashCode()和equals()方法,hashCode()的返回值尽可能的分散。
- 当HashMap的entry的数组足够大,key的hash值足够分散时,即是可以实现一个entry数组下标最多只对应了一个entry,此时get方法的时间复杂度可以达到O(1)。
- 在数组长度和get方法的速度上要达到一个平衡。数组比较长碰撞出现的概率就比较小,所以get方法获取值时就比较快,但浪费了比较多的空间;当数组长度没有冗余时,碰撞出现的概率比较大,虽然节省了空间,但会牺牲get方法的时间。
- HashMap有默认的装载因子loadFactor=0.75,默认的entry数组的长度为16。装载因子的意义在于使得entry数组有冗余,默认即允许25%的冗余,当HashMap的数据的个数超过12(16*0.75)时即会对entry数组进行第一次扩容,后面的再次扩容依次类推。
- HashMap每次扩容一倍,resize时会将已存在的值从新进行数组下标的计算,这个是比较浪费时间的。在平时使用中,如果能估计出大概的HashMap的容量,可以合理的设置装载因子loadFactor和entry数组初始长度即可以避免resize操作,提高put的效率。
- HashMap不是线程安全的,多线程环境下可以使用Hashtable或ConcurrentHashMap。
相关推荐
Java集合系列之HashMap源码分析 Java集合系列之HashMap源码分析是Java集合系列中的一篇非常重要的文章,它详细介绍了Java集合系列之HashMap源码,具有很高的参考价值。下面我们将对HashMap源码进行详细的分析。 ...
HashMap 是 Java 中最常用的集合类之一,它是基于哈希表实现的,提供了高效的数据存取功能。HashMap 的核心原理是将键(key)通过哈希函数转化为数组索引,然后将键值对(key-value pair)存储在数组的对应位置。...
在Java编程语言中,HashMap是集合框架中一个重要的类,用于存储键值对的数据结构。面试中,HashMap的源码分析与实现是一个常见的考察点,因为它涉及到数据结构、并发处理和性能优化等多个核心领域。本篇文章将深入...
HashMap是一种基于哈希表的集合类,它的存储结构是一个数组,每个元素是一个单链表的头节点,链表用来解决hash地址冲突问题。HashMap有四个构造方法,其中初始容量和加载因子是影响性能的重要参数。加载因子是哈希表...
Set接口在Java集合框架中扮演着重要角色,它是一个不包含重复元素的集合。Set接口继承自Collection接口,提供...了解和掌握这两种集合类的源码分析有助于深入理解Java集合框架的底层实现,从而更好地应用在实际开发中。
4. **源码分析:HashMap** `HashMap`是`Map`接口的主要实现,它使用哈希表(数组+链表/红黑树)来存储键值对。哈希函数用于快速定位元素,链表处理哈希冲突。当链表长度超过一定阈值时,会转换为红黑树,以提高查找...
《HashMap 源码解析——JDK11版本》 HashMap是Java中广泛使用的非同步散列表,其设计和实现是高效且灵活的。在JDK1.8之前,HashMap的底层数据...理解HashMap的源码对于深入学习Java集合框架和数据结构具有重要意义。
Java集合框架是Java编程语言中非常重要的组成部分,它为Java开发者提供了大量用于存储数据的结构。Java集合框架主要包括两大类,单列集合和双列集合。单列集合中,Set接口的集合主要用于存储不重复的元素,而List...
Java集合类源码分析之Set详解 Java集合类中的Set Interface是用于存储无序、不可重复元素的集合接口。Set Interface继承自Collection Interface,提供了基本的集合操作,如add、remove、contains等。Set Interface...
在多线程环境下,Java的`HashMap`类在处理并发操作时容易出现线程安全问题。本文档深入探讨了`HashMap`在多线程环境中可能遇到的安全问题,并提出了一系列可行的解决方案。 #### 二、HashMap线程安全问题分析 在多...
3. 集合类的实现:分析List类和Map类的内部结构,理解它们如何存储和操作数据。 4. 面向接口编程:虽然易语言没有像Java那样的接口概念,但可以通过模拟接口的实现,提供类似的功能。 5. 键值对操作:了解如何在Map...
2. `ArrayList` 和 `LinkedList` 类:这两个类都是`List`接口的实现,用于存储有序的元素集合。`ArrayList`基于动态数组,适用于随机访问,而`LinkedList`基于双向链表,适合于频繁插入和删除。通过源码分析,我们...
下面我们将从HashMap的优点、缺点、使用方法、源码分析等方面进行深入分析。 HashMap的优点 HashMap是一种超级快速的查询速度,时间复杂度可以达到O(1)的数据结构。同时,它也是一种动态的可变长存储数据结构,...
HashMap是Java编程语言中最常用的集合类之一,它用于存储键值对,并且基于哈希表实现。在JDK1.8之前,HashMap的数据结构由数组和链表共同组成,使用了"拉链法"来解决哈希冲突。数组是HashMap的基础,而链表则用于...
- `HashTable`继承自`Dictionary`类,而`HashMap`实现了`Map`接口,这反映了Java集合框架的发展历史,`Map`接口提供了更现代和灵活的API设计。 - `HashTable`的一些方法名使用了过时的命名约定,如`elements()`和`...
源码分析是学习Java集合的关键,它能让你看到集合类的实际运行过程。例如,通过查看ArrayList的add()方法源码,你可以了解到元素是如何被添加到数组中的,以及何时会触发数组扩容。同样,研究HashMap的put()方法,...
本文将深入剖析Java集合的源码,探讨其内部实现机制,并结合常见面试题,帮助你更好地理解和应用这些知识。 首先,我们从基础开始,Java集合框架主要分为两大类:List(列表)和Set(集合)。List接口包括ArrayList...
Java集合框架是Java编程语言中的一个核心部分,它为数据结构和对象的存储、管理和操作提供了统一...通过学习和分析`chapter3.html`这样的文档,我们可以深入了解每个集合类的内部工作原理,进一步提升我们的编程技能。
2. **集合框架**: `java.util`包中的`ArrayList`、`HashMap`、`LinkedList`等集合类的源码解析,可以让我们学习到数据结构和算法在Java中的应用,比如动态数组的扩容策略,哈希表的冲突解决方法。 3. **I/O流**: `...