1、HashMap类图
Map 用于保存具有映射关系的数据,因此 Map 集合里保存着两组值,一组值用于保存 Map 里的 Key,另外一组用于保存 Map 里的 Value,Map 中的 key 和 value 都可以是任何引用类型的数据。Map 中的 Key 不允许重复,即同一个 Map 对象的任何两个 Key 通过 equals 方法比较中返回 false,Key 和 Value 之间存在单向一对一关系,即通过指定的 Key 总能找到唯一的,确定的 Value。
HashMap按 Hash 算法来存储集合中的元素,因此具有很好的存取和查找性能。HashMap具有以下特点: –不能保证元素的排列顺序
–HashMap不是线程安全的
–集合元素可以使null值和null键
当向 HashMap集合中存入一个元素时,HashMap会调用该对象的 hashCode() 方法来得到该对象的 hashCode值,然后根据 hashCode值决定该对象在 HashMap中的存储位置。HashMap使用拉链法解决元素冲突。
在java编程语言中,最基本的结构就是两种,一个是数组,另外一个是模拟指针(引用),所有的数据结构都可以用这两个基本结构来构造的,HashMap也不例外。HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。
从上图中可以看出,HashMap底层就是一个数组结构,数组中的每一项又是一个链表。当新建一个HashMap的时候,就会初始化一个数组。
HashMap构造实现以及重要方法
1)底层使用数组实现: /**
* The table, resized as necessary. Length MUST Always be a power of two.
*/
transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;
)构造器
HashMap 提供了4个不同的构造器
// 构造一个初始大小为initialCapacity,装载因子为loadFactor的空的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();
}
// 构造一个初始大小为initialCapacity,装载因子为默认的0.75的空的HashMap
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
//默认的无参数构造器,默认的初始大小为16,装载因子为0.75
public HashMap() {
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}
public HashMap(Map<? extends K, ? extends V> m) {
this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
inflateTable(threshold);
putAllForCreate(m);
}
3)put(key: K, value: V): V 向Map中添加key-value对方法
// HashMap允许存放null键和null值。
// 当key为null时,调用putForNullKey方法,将value放置在数组第一个位置。
// 根据key的keyCode重新计算hash值,然后搜索指定hash值在对应table中的索引也就是对应的Hash桶。
// 如果 i 索引处的 Entry 不为 null,通过循环不断遍历 e 元素的下一个元素。 通过equals方法判断相等性,若发现已存在,则替换,否则新添加。
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;
}
// 真正向Map中添加Entry方法
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);
}
//计算hash值的算法,此算法加入了高位计算,防止低位不变,高位变化时,造成的hash冲突。
final int hash(Object k) {
int h = hashSeed;
if (0 != h && k instanceof String) {
return sun.misc.Hashing.stringHash32((String) k);
}
// 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).
return h ^ (h >>> 7) ^ (h >>> 4);
}
//通过此方法,将计算出来的Hash值对应到底层数组下标,计算出Hash桶。
static int indexFor(int h, int length) {
// assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2";
return h & (length-1);
}
4 ) get(key: Object): V 获取指定Key对应的Value方法
//HashMap中get元素时,首先计算key的hashCode,找到数组中对应位置的某一元素,然后通过key的equals方法在对应位置的链表中找到需要的元素。 public V get(Object key) {
if (key == null) //若key为null,则从table的第一个位置获取
return getForNullKey();
Entry<K,V> entry = getEntry(key);
return null == entry ? null : entry.getValue();
}
final Entry<K,V> getEntry(Object key) {
if (size == 0) {
return null;
}
int hash = (key == null) ? 0 : hash(key);
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 != null && key.equals(k))))
return e;
}
return null;
}
5) resize( newCapacity :int):void HashMap的扩容方法
当HashMap中的元素越来越多的时候,hash冲突的几率也就越来越高,因为数组的长度是固定的。所以为了提高查询的效率,就要对HashMap的数组进行扩容,数组扩容这个操作也会出现在ArrayList中,这是一个常用的操作,而在HashMap数组扩容之后,最消耗性能的点就出现了:原数组中的数据必须重新计算其在新数组中的位置,并放进去,这就是resize。
那么HashMap什么时候进行扩容呢?当HashMap中的元素个数超过数组大小*loadFactor时,就会进行数组扩容,loadFactor的默认值为0.75,这是一个折中的取值。也就是说,默认情况下,数组大小为16,那么当HashMap中元素个数超过16*0.75=12的时候,就把数组的大小扩展为 2*16=32,即扩大一倍,然后重新计算每个元素在数组中的位置,而这是一个非常消耗性能的操作,所以如果我们已经预知HashMap中元素的个数,那么预设元素的个数能够有效的提高HashMap的性能。
void resize(int newCapacity) {
Entry[] oldTable = table;
int oldCapacity = oldTable.length;
if (oldCapacity == MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return;
}
Entry[] newTable = new Entry[newCapacity];
transfer(newTable, initHashSeedAsNeeded(newCapacity));
table = newTable;
threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
}
/**
* Transfers all entries from current table to newTable.
*/
void transfer(Entry[] newTable, boolean rehash) {
int newCapacity = newTable.length;
for (Entry<K,V> e : table) {
while(null != e) {
Entry<K,V> next = e.next;
if (rehash) {
e.hash = null == e.key ? 0 : hash(e.key);
}
int i = indexFor(e.hash, newCapacity);
e.next = newTable[i];
newTable[i] = e;
e = next;
}
}
}
6) remove(key: Object): V 从Map中移除对应的Key方法
public V remove(Object key) {
Entry<K,V> e = removeEntryForKey(key);
return (e == null ? null : e.value);
}
public V remove(Object key) {
Entry<K,V> e = removeEntryForKey(key);
return (e == null ? null : e.value);
}
final Entry<K,V> removeEntryForKey(Object key) {
if (size == 0) {
return null;
}
int hash = (key == null) ? 0 : hash(key);
int i = indexFor(hash, table.length);
Entry<K,V> prev = table[i];
Entry<K,V> e = prev;
while (e != null) {
Entry<K,V> next = e.next;
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) {
modCount++;
size--;
if (prev == e)
table[i] = next;
else
prev.next = next;
e.recordRemoval(this);
return e;
}
prev = e;
e = next;
}
return e;
}
7) keySet(): Set<K> 返回该HashMap的所以Key的集合视图方法
,并返回,在遍历这个Set视图时,通过iterator()方法,获取newKeyIterator();
public Set<K> keySet() {
Set<K> ks = keySet;
return (ks != null ? ks : (keySet = new KeySet()));
}
private final class KeySet extends AbstractSet<K> {
public Iterator<K> iterator() {
return newKeyIterator();
}
public int size() {
return size;
}
public boolean contains(Object o) {
return containsKey(o);
}
public boolean remove(Object o) {
return HashMap.this.removeEntryForKey(o) != null;
}
public void clear() {
HashMap.this.clear();
}
}
8) entrySet(): Set<Map.Entry<K,V>> 获取hashMap中包含的元素视图集合方法
public Set<Map.Entry<K,V>> entrySet() {
return entrySet0();
}
private Set<Map.Entry<K,V>> entrySet0() {
Set<Map.Entry<K,V>> es = entrySet;
return es != null ? es : (entrySet = new EntrySet());
}
private final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
public Iterator<Map.Entry<K,V>> iterator() {
return newEntryIterator();
}
public boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<K,V> e = (Map.Entry<K,V>) o;
Entry<K,V> candidate = getEntry(e.getKey());
return candidate != null && candidate.equals(e);
}
public boolean remove(Object o) {
return removeMapping(o) != null;
}
public int size() {
return size;
}
public void clear() {
HashMap.this.clear();
}
}
9) Fail-Fast机制:
我们知道java.util.HashMap不是线程安全的,因此如果在使用迭代器的过程中有其他线程修改了map,那么将抛出ConcurrentModificationException,这就是所谓fail-fast策略。
这一策略在源码中的实现是通过modCount域,modCount顾名思义就是修改次数,对HashMap内容的修改都将增加这个值,那么在迭代器初始化过程中会将这个值赋给迭代器的expectedModCount。
HashIterator() {
expectedModCount = modCount;
if (size > 0) { // advance to first entry
Entry[] t = table;
while (index < t.length && (next = t[index++]) == null)
;
}
}
在迭代过程中,判断modCount跟expectedModCount是否相等,如果不相等就表示已经有其他线程修改了Map。注意到modCount声明为volatile,保证线程之间修改的可见性。
final Entry<K,V> nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
Entry<K,V> e = next;
if (e == null)
throw new NoSuchElementException();
if ((next = e.next) == null) {
Entry[] t = table;
while (index < t.length && (next = t[index++]) == null)
;
}
current = e;
return e;
}
0 0
- 深入学习java集合:HashMap<K,V>实现
- 深入学习java集合:LinkedHashMap<K,V>实现
- 深入学习java集合:Hashtable<K,V>实现
- Java集合之Hashtable<K,V>,TreeMap<K,V>,HashMap<K,V>自动排序
- 深入学习java并发编程:ConcurrentHashMap<K, V>实现
- 深入Java集合学习系列:HashMap的实现原理
- 深入Java集合学习系列:HashMap的实现原理
- 深入Java集合学习系列:HashMap的实现原理
- 深入Java集合学习系列:HashMap的实现原理
- 深入Java集合学习系列:HashMap的实现原理
- 深入Java集合学习系列:HashMap的实现原理
- 深入Java集合学习系列:HashMap的实现原理
- 深入Java集合学习系列:HashMap的实现原理
- 深入Java集合学习系列:HashMap的实现原理
- 深入Java集合学习系列:HashMap的实现原理
- 深入Java集合学习系列:HashMap的实现原理
- 深入Java集合学习系列:HashMap的实现原理
- 深入Java集合学习系列:HashMap的实现原理
- MySQL索引类型总结和使用技巧以及注意事项
- 短信发送器
- 用html5-canvas画可以走动的时钟
- Duilib学习笔记《02》—界面布局
- 支付宝的支付流程
- 深入学习java集合:HashMap<K,V>实现
- 隐马尔可夫模型简单介绍
- Hibernate 中执行 对Oracle 数据库执行 save 方式是报 序列不存在 的问题?
- 练习三 Problem M
- C++标准库——random
- win10 edge打不开,解决方案
- 【hibernate进阶】hibernate持久化对象的三种状态
- Weka中文乱码解决方法
- 三个故事