HashMap学习
Map 接口以及主要实现类
Map接口提供三类集合视图
- 键(key) 集合
- 值(value) 集合
- 键值对(key-value) 集合
public interface Map<K, V> {
Set<K> keySet();
Collection<V> values();
Set<Map.Entry<K, V>> entrySet();
}
AbstractMap类提供了Map接口的框架实现
大多数Map具体实现类均基于 AbstractMap
类扩展其所需要的方法实现。
// Example
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {
// methods
}
Map接口主要方法介绍
int size()
: 返回Map中键值对个数boolean isEmpty()
: 返回Map是否为空boolean containsKey(Object key)
: 返回Map是否包含指定键keyboolean containsValue(Object value)
: 返回Map是否包含指定值valueV get(Object key)
: 返回指定key对应的值,若key不存在则返回nullV put(K key, V value)
: 插入键值对,若指定key原本就在Map中,则更新key的值,并返回旧的值;若指定key原本不在Map中,则插入该键值对,并返回nullV remove(Object key)
: 移除键值对,若Map中存在指定key,则移除该键值对,并返回对应的值value;若Map中不存在指定key,则返回nullvoid clear()
: 删除Map中所有键值对boolean equals(Object o)
: 返回两个Map是否相同int hashCode()
: 计算当前Map的哈希值,hash(map) = sum(hash(key))
public interface Map<K,V> {
int size();
boolean isEmpty();
boolean containsKey(Object key);
boolean containsValue(Object value);
V get(Object key);
V put(K key, V value);
V remove(Object key);
// 拷贝m中的键值对到当前Map,每个元素执行put操作
void putAll(Map<? extends K, ? extends V> m);
void clear();
boolean equals(Object o);
int hashCode();
/**
* 默认方法 Default
* 1.可以写方法内容
* 2.不强制实现类重写
* 3.接口的实现可以直接调用default方法
*/
// 查找指定key的值,若key不存在则返回默认值
default V getOrDefault(Object key, V defaultValue) {
V v;
return (((v = get(key)) != null) || containsKey(key))
? v
: defaultValue;
}
// 使用指定action.accept()方法处理所有键值对
default void forEach(BiConsumer<? super K, ? super V> action) {
Objects.requireNonNull(action);
for (Map.Entry<K, V> entry : entrySet()) {
K k;
V v;
try {
k = entry.getKey();
v = entry.getValue();
} catch(IllegalStateException ise) {
// this usually means the entry is no longer in the map.
throw new ConcurrentModificationException(ise);
}
action.accept(k, v);
}
}
// 使用指定function.apply()方法更新所有键值对
default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
Objects.requireNonNull(function);
for (Map.Entry<K, V> entry : entrySet()) {
K k;
V v;
try {
k = entry.getKey();
v = entry.getValue();
} catch(IllegalStateException ise) {
// this usually means the entry is no longer in the map.
throw new ConcurrentModificationException(ise);
}
// ise thrown from function is not a cme.
v = function.apply(k, v);
try {
entry.setValue(v);
} catch(IllegalStateException ise) {
// this usually means the entry is no longer in the map.
throw new ConcurrentModificationException(ise);
}
}
}
// 如果Map中不包含指定key,则插入该键值对;否则返回指定key对应的值
default V putIfAbsent(K key, V value) {
V v = get(key);
if (v == null) {
v = put(key, value);
}
return v;
}
// 移除指定键值对
default boolean remove(Object key, Object value) {
Object curValue = get(key);
if (!Objects.equals(curValue, value) ||
(curValue == null && !containsKey(key))) {
return false;
}
remove(key);
return true;
}
// 更新指定键值对
default boolean replace(K key, V oldValue, V newValue) {
Object curValue = get(key);
if (!Objects.equals(curValue, oldValue) ||
(curValue == null && !containsKey(key))) {
return false;
}
put(key, newValue);
return true;
}
// 更新指定键值对并返回旧的值value
default V replace(K key, V value) {
V curValue;
if (((curValue = get(key)) != null) || containsKey(key)) {
curValue = put(key, value);
}
return curValue;
}
}
HashMap源码阅读
HashMap是使用最频繁的Map实现类之一,主要用于存放键值对,允许 key 和 value 为null值,不保证元素顺序,线程不安全,使用内部类
Node <K, V>
存在元素。
- HashMap使用 数组 + 链表/红黑树 的结构存储元素
- 当链表长度大于阈值(默认值8)时:
- 当前数组长度小于 MIN_TREEIFY_CAPACITY,则对数组扩容(2倍)
- 当前数组长度不小于 MIN_TREEIFY_CAPACITY,则转换为红黑树
HashMap主要属性介绍
public class HashMap<K, V> extends AbstractMap<K, V> implements Map<K, V>, Cloneable, Serializable {
// 序列号,用于Java序列化
private static final long serialVersionUID = 362498820763181265L;
// 默认初始化容量 16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
// HashMap最大容量 2^30 - 1
static final int MAXIMUM_CAPACITY = 1 << 30;
/**
* 默认负载因子 0.75
* count(hash_map) > load_factor * capacity: 触发扩容
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;
// 对于哈希冲突产生的链表结构,将其转换为树结构的阈值
static final int TREEIFY_THRESHOLD = 8;
// 上面转换操作的逆操作阈值
static final int UNTREEIFY_THRESHOLD = 6;
/**
* 使用树结构的最小容量
* 如果table容量小于64且链表长度超过设置的阈值,则优先对table扩容
*/
static final int MIN_TREEIFY_CAPACITY = 64;
// 存储键值对的数组,容量总是2的幂次方
transient Node<K,V>[] table;
// 存储键值对的集合
transient Set<Map.Entry<K,V>> entrySet;
// map中键值对的数量
transient int size;
// map扩容和结构改变的次数
transient int modCount;
// 扩容阈值 LOAD_FACTOR * CAPACITY
int threshold;
// 加载因子
final float loadFactor;
}
HashMap构造方法介绍
/**
* 默认构造函数
* 使用默认的载入因子 0.75
* 使用默认容量 16
* put操作时才初始化table数组
*/
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR;
}
/**
* 自定义初始容量
*/
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
/**
* 自定义初始容量和加载因子
*/
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;
// 返回2的幂次方,如initialCapacity=5,则threshold=8
this.threshold = tableSizeFor(initialCapacity);
}
/**
* 使用Map m初始化要构造的HashMap
*/
public HashMap(Map<? extends K, ? extends V> m) {
this.loadFactor = DEFAULT_LOAD_FACTOR;
putMapEntries(m, false);
}
HashMap核心方法介绍
插入键值对 —— V put(K key, V value)方法
// 计算 key 的哈希值
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
public V put(K key, V value) {
// 调用 putVal 方法插入元素
return putVal(hash(key), key, value, false, true);
}
/**
* 缺省 访问控制修饰符,仅供包内使用,用户无法调用
* @param hash: key的哈希值
* @param key: 插入的键
* @param value: 插入的值
* @param onlyIfAbsent: ture -> 不更新已存在的键值对
* @param evict: 在HashMap中不做处理
* @return: 返回旧的value值,若不存在则返回Null
*/
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab;
Node<K,V> p;
int n, i;
// 如果 table 未初始化或长度为0,则使用 resize()方法初始化和扩容
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
/**
* tab[i = (n - 1) & hash]: 计算键值对应该存储的table位置
* 1.若当前位置还没有元素,则直接添加
* 2.若当前位置已有元素,则判断当前要插入的 key 和已存在元素的 key 是否一致
* 3.如果一致,则直接更新该元素的value
* 4.如果不一致,则插入链表尾部或插入红黑树
*/
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e;
K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
/**
* putIfAbsent()方法
* 在 put()方法的基础上,将onlyIfAbsent设置true调用putval()方法
* 即map中已存在key时,不更新该元素,并返回该key在map中的value值
*/
public V putIfAbsent(K key, V value) {
return putVal(hash(key), key, value, true, true);
}
查询键值对 —— V get(K key) 方法
public V get(Object key) {
Node<K,V> e;
// 调用 getNode() 方法查询元素
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
/**
* 缺省 访问控制修饰符,仅供包内使用,用户无法调用
* @param hash: key的哈希值
* @param key: 键
* @return: 返回查询到的键值对Node
*/
final Node<K,V> getNode(int hash, Object key) {
Node<K,V>[] tab;
Node<K,V> first, e;
int n;
K k;
// tab[(n - 1) & hash] 该位置为 key 的哈希索引位置
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) {
// 比较该位置第1个元素与查询key
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k))))
return first;
// 如果第1个元素不是要找的key,则遍历整个链表或红黑树查找
if ((e = first.next) != null) {
if (first instanceof TreeNode)
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
do {
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
// 若未找到则返回null
return null;
}
/**
* getOrDefault() 方法
* 该方法在get()方法的基础上,添加了默认返回值。
* 若 key 存在 map 中,则反回对应的 value
* 若 key 不存在,则返回默认值 defaultValue
*/
public V getOrDefault(Object key, V defaultValue) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? defaultValue : e.value;
}
删除键值对 —— V remove(Object key) 方法
public V remove(Object key) {
Node<K,V> e;
return (e = removeNode(hash(key), key, null, false, true)) == null ? null : e.value;
}
/**
* 缺省 访问控制修饰符,仅供包内使用,用户无法调用
* @param hash: key的哈希值
* @param key: 键
* @param value: 值
* @param matchValue: 是否需要匹配value,true->键在map中的value值和传入的参数value匹配才会删除该键值对
* @param movable: 是否要移动其它节点
*/
final Node<K,V> removeNode(int hash, Object key, Object value,
boolean matchValue, boolean movable) {
Node<K,V>[] tab;
Node<K,V> p;
int n, index;
if ((tab = table) != null && (n = tab.length) > 0 &&
(p = tab[index = (n - 1) & hash]) != null) {
Node<K,V> node = null, e;
K k;
V v;
/**
* 查询指定的Key
*/
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
node = p;
else if ((e = p.next) != null) {
if (p instanceof TreeNode)
node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
else {
do {
if (e.hash == hash &&
((k = e.key) == key ||
(key != null && key.equals(k)))) {
node = e;
break;
}
p = e;
} while ((e = e.next) != null);
}
}
/**
* 删除指定键值对
*/
if (node != null && (!matchValue || (v = node.value) == value ||
(value != null && value.equals(v)))) {
if (node instanceof TreeNode)
((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
else if (node == p)
tab[index] = node.next;
else
p.next = node.next;
++modCount;
--size;
afterNodeRemoval(node);
return node;
}
}
return null;
}
HashMap简单使用
package hash_map;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class HashMapExamle {
public static void main(String[] args) {
// 使用默认构造函数, 默认容量为16, 载入因子为 0.75
HashMap<String, String> map = new HashMap<>();
for (int i = 0; i < 20; i++) {
// 添加第13个元素时触发扩容:16 * 0.75 = 12
map.put(i + "", "Example " + i);
}
// 支持null作为key和value
map.put(null, "Null key");
map.put("Null value", null);
map.remove("0");
// get获取指定键对应的值, 若不存在则返回null
// getOrDefault获取指定键对应的值, 若不存在在则返回默认值
System.out.println(map.get("2")); // Example 2
System.out.println(map.get("20") + " " + map.getOrDefault("20", "Default Value")); // null Default Value
System.out.println(map.containsKey("2") + " " + map.containsKey("20")); // true false
System.out.println(map.containsValue("Example 2") + " " + map.containsKey("Example 20")); //true false
// entrySet()遍历哈希表
Set<Map.Entry<String, String>> entrySet = map.entrySet();
for (Map.Entry<String, String> entry : entrySet) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
// keySet()遍历哈希表
Set<String> keySet = map.keySet();
for (String key : keySet) {
System.out.println(key);
}
// valueSet()获取所有value值
Collection<String> values = map.values();
for (String value : values) {
System.out.println(value);
}
}
}
参考文章: