HashMap、LinkedHashMap、HashSet、LinkedHashSet 原理解析及关系梳理

本文以jdk源码为线索学习几种数据类型实现机制。

【HashMap数据机制】

HashMap提供了key、value存储机制。
HashMap是LinkedHashMap的基类,其内部维护一个Node数组用来存储数据:

transient Node<K,V>[] table;

为了解决hash冲突,每个节点存储链表或者红黑树。当链表长度小于阈值8时,使用链表存储;当长度达到阈值8时,将链表转换为红黑树提升读写效率:

if ((e = p.next) == null) {
	p.next = newNode(hash, key, value, null);
	if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
		treeifyBin(tab, hash);
}

A. put

public V put(K key, V value);

put数据时,首先依据key计算hash值,与数组尺寸做与运算,计算存储数据在数组中的位置。
若该位置没有数据(未发生hash冲突),直接依据hash值、key、value构造Node存入该位置:

if ((p = tab[i = (n - 1) & hash]) == null)
	tab[i] = newNode(hash, key, value, null);

当存在冲突时,遍历链表/红黑树查找当前key是否存在。若key已经存在,则替代旧的value:

V oldValue = e.value;
e.value = value;
afterNodeAccess(e);
return oldValue;

否则,将新的数据存入链表/红黑树:
插入链表:

if ((e = p.next) == null) {
	p.next = newNode(hash, key, value, null);

插入红黑树:

e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);

在插入数据之后,执行回调方法:

afterNodeInsertion(evict);

当数据数量达到一定阈值时,resize()方法将被执行,对数组进行扩容。

Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;

B. remove

public V remove(Object key);

执行remove方法时,将首先依据要移除key的hash值定位在数组中的位置,
之后依据Node类型是TreeNode或链表对key进行查找。
当匹配上时,将node从链表或tree中移除,并将其value作为remove方法返回值。
在remove之后,调用回调方法:

afterNodeRemoval(node);

【linkedHashMap】

为了解决hashMap无序的问题,引入了有序存储key、value的数据结构LinkedHashMap。
linkedHashMap以HashMap为基类,提供了记录插入key、value顺序的功能,读取时将按序输出。

记录的节点以双向链表的形式存储:

transient LinkedHashMap.Entry<K,V> head;
transient LinkedHashMap.Entry<K,V> tail;

A. put

基类HashMap中当插入数据时,构造新节点时执行newNode方法。
LinkedHashMap重写了newNode方法:

Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
	LinkedHashMap.Entry<K,V> p =
		new LinkedHashMap.Entry<K,V>(hash, key, value, e);
	linkNodeLast(p);
	return p;
}

每一个put的key、value被构造成双向链表节点p,最终被接在tail节点的后面,成为新的tail节点:

LinkedHashMap.Entry<K,V> last = tail;
tail = p;
p.before = last;
last.after = p;

若put之前存在相同key的旧值,那么就需要将旧值移除。
在插入完成之后,HashMap中会调用afterNodeInsertion方法,LinkedHashMap中实现此模板方法,将双向链表中旧的节点移除。

B. remove

基类HashMap中当remove被调用,将相应key的节点移除后,回调方法afterNodeRemoval将被调用。
LinkedHashMap重写了此方法,将相应key的节点从双向链表中移除。

C. 读取

LinkedHashMap内部的迭代器iterator以双向链表head为起点遍历输出,从而实现有序输出的属性。

【HashSet】

HashSet实现了Set接口,存储一组不重复的元素。
为了实现不重复的功能,其内部使用HashMap存储数据,利用其key的唯一性:

public HashSet() {
	map = new HashMap<>();
}

执行add元素时,

public boolean add(E e) {
	return map.put(e, PRESENT)==null;
}

执行remove移除元素时,

public boolean remove(Object o) {
	return map.remove(o)==PRESENT;
}

遍历元素时,直接使用HashMap的KeySet():

public Iterator<E> iterator() {
	return map.keySet().iterator();
}

由于HashMap内部数据依据hash值在数组中存储,因此HashSet的iterator也是无序的。

【LinkedHashSet】

为了解决HashSet无序存储的问题,引入了LinkedHashSet。

其内部利用了LinkedHashMap有序的特性:

HashSet(int initialCapacity, float loadFactor, boolean dummy) {
	map = new LinkedHashMap<>(initialCapacity, loadFactor);
}

其迭代器返回LinkedHashMap的KeySet,从而实现有序输出。

原文地址:https://www.cnblogs.com/xinxinBlog/p/10347259.html

时间: 2024-10-10 08:53:52

HashMap、LinkedHashMap、HashSet、LinkedHashSet 原理解析及关系梳理的相关文章

Java HashMap LinkedHashMap 区别及原理

HashMap原理 HashMap是Map的一个常用的子类实现.其实使用散列算法实现的. HashMap内部维护着一个散列数组(就是一个存放元素的数组),我们称其为散列桶,而当我们向HashMap中存入一组键值对时,HashMap首先获取key这个对象的hashcode()方法的返回值,然后使用该值进行一个散列算法,得出一个数字,这个数字就是这组键值对要存入散列数组中的下标位置. 那么得知了下标位置后,HashMap还会查看散列数组当前位置是否包含该元素.(这里要注意的是,散列数组中每个元素并非

HashMap源码及原理解析

1.HashMap简介 HashMap提供所有可选的Map操作,并允许使用 null 值和 null 键,是线程不安全的.(除了非同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同.)此类不保证映射的顺序,特别是它不保证该顺序恒久不变. HashMap的实例有两个参数影响其性能:初始容量 和加载因子.容量 是哈希表中桶的数量,初始容量只是哈希表在创建时的容量.加载因子 是哈希表在其容量自动增加之前可以达到多满的一种尺度.当哈希表中的条目(或者说元素)数超出了加载因

HashMap,LinkedHashMap等实现原理

参考 HashMap http://blog.csdn.net/kimylrong/article/details/21654405 http://blog.csdn.net/vking_wang/article/details/14166593 LinkedHashMap http://www.cnblogs.com/children/archive/2012/10/02/2710624.html http://zhangshixi.iteye.com/blog/673789 对比  http

java该HashTable,HashMap和HashSet

同一时候我们也对HashSet和HashMap的核心方法hashcode进行了具体解释,见<探索equals()和hashCode()方法>. 万事俱备,那么以下我们就对基于hash算法的三个集合HashTable,HashSet和HashMap具体解释. 本文文件夹: 1. HashTable和HashMap的差别 2. HashSet和HashMap的差别 3. HashMap,HashSet工作原理 4. HashSet工作原理 5. 常见问题 1. HashTable和HashMap的

java中的HashTable,HashMap和HashSet

目录(?)[+] 上篇博客中我们详细的分析了java集合<java中Map,List与Set的区别>. 同时我们也对HashSet和HashMap的核心方法hashcode进行了详解,见<探索equals()和hashCode()方法>. 万事俱备,那么下面我们就对基于hash算法的三个集合HashTable,HashSet和HashMap详解. 本文目录: 1. HashTable和HashMap的区别 2. HashSet和HashMap的区别 3. HashMap,HashS

HashMap和HashSet解析

------------------------------------------------HashMap------------------------------------------------------ 一.---概念--- HashMap继承自AbstractMap,实现了Map接口.下面从定义入手来开始分析: public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>,

HashMap底层实现原理/HashMap与HashTable区别/HashMap与HashSet区别

Hash算法 Hash,一般翻译做"散列",也有直接音译为"哈希"的,就是把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值.这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,而不可能从散列值来唯一的确定输入值.简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数. HASH主要用于信息安全领域中加密算法,它把一些不同长度的信息转化成杂乱的128

[转]HashMap与HashTable的区别、HashMap与HashSet的关系

转自: http://blog.csdn.net/wl_ldy/article/details/5941770 HashTable的应用非常广泛,HashMap是新框架中用来代替HashTable的类,也就是说建议使用HashMap,不要使用HashTable.可能你觉得HashTable很好用,为什么不用呢?这里简单分析他们的区别. 一:HashMap与HashTable的区别 1.HashTable的方法是同步 的,在方法的前面都有synchronized来同步,HashMap未经同步,所以

HashMap与HashTable的区别、HashMap与HashSet的关系

http://blog.csdn.net/wl_ldy/article/details/5941770 HashTable的应用非常广泛,HashMap是新框架中用来代替HashTable的类,也就是说建议使用HashMap,不要使用HashTable.可能你觉得HashTable很好用,为什么不用呢?这里简单分析他们的区别. 一:HashMap与HashTable的区别 1.HashTable的方法是同步 的,在方法的前面都有synchronized来同步,HashMap未经同步,所以在多线程