1 底层结构
-
JDK 1.7
HashMap
的底层结构是由数组+链表
构成的 -
JDK 1.8
HashMap
的底层结构是由数组+红黑树
构成的
-
数组
(紫色):hash
数组(桶),数组元素是每个链表的头节点 -
链表
(绿色):解决hash
冲突,不同的key
映射到了数组的同一索引处,则形成链表。
2 put
和 get
方法
put()
方法大概过程如下:
-
如果添加的
key
值为null
,那么将该键值对添加到数组索引为0的链表中,不一定是链表的首节点。 -
如果添加的
key
不为null
,则根据key
计算数组索引的位置:- 数组索引处存在链表,则遍历该链表,如果发现
key
已经存在,那么将新的value
值替换旧的value
值 - 数组索引处不存在链表,将该
key-value
添加到此处,成为头节点
get()
方法的大概过程:
- 数组索引处存在链表,则遍历该链表,如果发现
-
如果
key
为null
,那么在数组索引table[0]
处的链表中遍历查找key
为null
的value
-
如果
key
不为null
,根据key
找到数组索引位置处的链表,遍历查找key
的value
,找到返回value
,若没找到则返回null
3 扩容机制
先看一个例子,创建一个 HashMap
,初始容量默认为 16
,负载因子默认为 0.75
,那么什么时候它会扩容呢?
来看以下公式:
实际容量 = 初始容量 × 负载因子
计算可知,16×0.75=12
,也就是当实际容量超过 12
时,这个 HashMap
就会扩容。
3.1 初始容量
当构造一个 hashmap
时,初始容量设为不小于指定容量的2的次方的一个数(new HashMap(5)
, 指定容量为5,那么实际初始容量为8,2^3=8>5
),且最大值不能超过2的30次方。
3.2 负载因子
负载因子是哈希数组在其容量自动增加之前可以达到多满的一种尺度。(时间与空间的折衷) 当哈希数组中的条目数超出了加载因子与初始容量的乘积时,则要对该哈希数组进行扩容操作(即resize)。
特点:
1*16=16
0.75*16=12 0.5*16=8
负载因子越小, 容易扩容—容易扩容的时候 产生新的空数组位置
负载因子越小,容易扩容,浪费空间,但查找效率高 链表特别短 减少hash冲突
负载因子越大,不易扩容,对空间的利用更加充分,查找效率低(链表拉长)hash冲突比较多,链表比较长
3.3 扩容过程
HashMap
在扩容时,新数组的容量将是原来的2倍,由于容量发生变化,原有的每个元素需要重新计算数组索引 Index
,再存放到新数组中去,这就是所谓的 rehash
。
3.4 eqauls
方法和 hashCode
方法
如果两个对象相同,那么它们的 hashCode
值一定要相同。也告诉我们重写 equals
方法,一定要重写 hashCode
方法,也就是说 hashCode
值要和类中的成员变量挂上钩,对象相同–>成员变量相同—->hashCode值一定相同
。
如果两个对象的 hashCode
相同,它们并不一定相同,这里的对象相同指的是用 eqauls
方法比较。
4 基于 Arraylist
实现 HashMap
集合
package com.snow.list;
import java.util.ArrayList;
import java.util.List;
/**
* 基于Arraylist实现hashMap集合<br>
* 缺点:性能特别低<br>
*
*/
public class ExtArraylistHashMap<Key, Value> {
// MAP 存储容量
private List<Entry<Key, Value>> tables = new ArrayList<Entry<Key, Value>>();
// map容器实际容量
// private int size;
// key、value
public void put(Key key, Value value) {
Entry entry = getEntry(key);
if (entry != null) {
// 已经存在
entry.value = value;
} else {
Entry newEntry = new Entry(key, value);
// 2.调用put的时候,将该hash存储对象存入到Arraylist中
tables.add(newEntry);
}
}
public Value get(Key key) {
Entry<Key, Value> entry = getEntry(key);
return entry == null ? null : entry.value;
}
public Entry<Key, Value> getEntry(Key key) {
// 从头查询到尾做优化### 准
for (Entry<Key, Value> entry : tables) {
if (entry.key.equals(key)) {
return entry;
}
}
return null;
}
public static void main(String[] args) {
ExtArraylistHashMap hashMap = new ExtArraylistHashMap<String, String>();
hashMap.put("a", "aaaa");
hashMap.put("b", "bbbb");
hashMap.put("a", "cccc");// key 相同的情况下, 会覆盖
System.out.println(hashMap.get("a"));
}
}
// hash存储对象
class Entry<Key, Value> {
// hashMap集合存储的key
Key key;
// hashMap集合存储的value
Value value;
public Entry(Key key, Value value) {
super();
this.key = key;
this.value = value;
}
}
控制台打印:
cccc
5 基于 LinkedList
实现 HashMap
(效率低)
package com.snow.list;
import java.util.LinkedList;
/**
* 基于LinkedList实现HashMap(效率低)<br>
* jdk 1.7的时候,hasmap使用数组+链表方式实现<br>
*
*/
@SuppressWarnings("rawtypes")
public class LinkedListHashMap {
// Map存放Entry对象
@SuppressWarnings("unchecked")
LinkedList<Entry>[] tables = new LinkedList[998];
// 如果hashCode相同的对象链表结合中
@SuppressWarnings("unchecked")
public void put(Object key, Object value) {
Entry newEntry = new Entry(key, value);
int hashCode = key.hashCode();// 97
// hash取模,获取余数
int hash = hashCode % tables.length;// 998 998以内 00997
// 1.获取该下标元素,是否有LinkedList
LinkedList<Entry> entryLinkedList = tables[hash];
if (entryLinkedList == null) {
// 没有hash冲突
entryLinkedList = new LinkedList<Entry>();
entryLinkedList.add(newEntry);
// tables数组
tables[hash] = entryLinkedList;
} else {
for (Entry entry : entryLinkedList) {
if (entry.key.equals(key)) {
// equals相等,hashCode 一定相同 说明:是同一个对象
entry.value = value;// 直接覆盖
} else {
// hashCode 相同,对象的值不一定相同
// 发生hash冲突问题,直接在后面继续添加链表节点
entryLinkedList.add(newEntry);
}
}
}
}
// hash算法
// System.out.println(hash);
// 两个对象做比较的时候,如果hashCode相同,对象的值是否一定相同 不一定相同
// 两个对象做比较的时候,如果 equals比较相同,对象的值是否一定相同 相同
// 查询直接使用hash值直接定位在数组那个位置
public Object get(Object key) {
int hashCode = key.hashCode();// 97
// hash取模,获取余数
int hash = hashCode % tables.length;// 998 998以内 00997
LinkedList<Entry> linkedList = tables[hash];
for (Entry entry : linkedList) {
if (entry.key.equals(key)) {
return entry.value;
}
}
return tables[hash];
}
public static void main(String[] args) {
LinkedListHashMap linkedListHashMap = new LinkedListHashMap();
linkedListHashMap.put("a", "aaaa");
linkedListHashMap.put("a", "ccccc");// 覆盖
System.out.println(linkedListHashMap.get("a"));
}
}
// hash存储对象
class Entry<Key, Value> {
// hashMap集合存储的key
Key key;
// hashMap集合存储的value
Value value;
public Entry(Key key, Value value) {
super();
this.key = key;
this.value = value;
}
}
控制台打印:
ccccc