Map接口
包位置 (java.util.Map)
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); // 移除某键对应的键值对
// 批量操作
void putAll(Map<? extends K, ? extends V> m); // 从指定的map中复制所有的键值对并添加
void clear(); // 清空当前map中的所有键值对
Set<K> keySet(); // 返回map中键的集合(不可在通过该集合迭代map时,对map进行改动)
Collection<V> values(); // 返回值的集合(不可在通过该集合迭代map时,对map进行改动)
Set<Map.Entry<K, V>> entrySet(); // 返回键值对的集合
interface Entry<K,V> {...}
boolean equals(Object o);
int hashCode();
// 默认实现方法
...
}
Map接口实现
注意是Hashtable,不是HashTable, 这里面有历史遗留问题。
1.Hashtable(java.util.HashTable)
public class Hashtable<K,V>
extends Dictionary<K,V> // 继承Dictionary父类
implements Map<K,V>, Cloneable, java.io.Serializable { // 实现Map接口、Cloneable接口、序列化接口
private transient Entry<?,?>[] table; // 数组存放 hashtable 中键值对数据
private transient int count; // 键值对对数
private int threshold; // 设定的阈值(当count超过设定的阈值时,hashtable扩容)
private float loadFactor; // 负载系数(默认情况下0.75f)
private transient int modCount = 0; // 记录hashtable结构改动次数(包括键值对数目变动、内部结构改动)
private static final long serialVersionUID = 1421746759512286392L;
**// 构造函数(默认情况下,容量设定为11,负载系数设定为0.75f) **
public Hashtable(int initialCapacity, float loadFactor) { ...} // 初始化容量和负载系数,阈值=容量*负载系数
public Hashtable(int initialCapacity) {
this(initialCapacity, 0.75f);
}
public Hashtable() {
this(11, 0.75f);
}
public Hashtable(Map<? extends K, ? extends V> t) {... } // 以t中的键值对初始化,容量为max(2*t大小, 11), 负载系数为默认值
public synchronized int size() {...} // 键值对数目
public synchronized boolean isEmpty() {...} // 是否为空
public synchronized Enumeration<K> keys() {...}
public synchronized Enumeration<V> elements() {...}
public synchronized boolean contains(Object value) {...} // 是否存在value与hashtable中某一键值对的值满足equals()
public boolean containsValue(Object value) {... } // 调用了上一个函数
public synchronized boolean containsKey(Object key) {... } // 根据key的hash值去判断hashtable中是否存在相对应的键(hash值一致,且同时要满足equals())
public synchronized V get(Object key) {...} // 用键取值,不存在相同的键则返回null
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; // 可分配的hashtable最大容量
protected void rehash() {... } // 键值对数目超过阈值时调用,完成扩容(2*旧容量+1)及对旧数据的复制
private void addEntry(int hash, K key, V value, int index) {... }
public synchronized V put(K key, V value) {... } // 添加键值对(键、值均不为空,否则报空指针异常),若键已存在,更新值并返回旧的值,内部调用上一函数
public synchronized V remove(Object key) {... } // 根据键删除键值对(键不能为null,否则报空指针异常),若键存在,删除并返回原来对应值;若键不存在且不为空,返回null
public synchronized void putAll(Map<? extends K, ? extends V> t) {... } // 逐一添加map t 中的键值对
public synchronized void clear() {...} // 清空键值对,并把键值对数目清零
public synchronized Object clone() {... } // 拷贝,但内部的键和值都只是浅拷贝
public synchronized String toString() {...}
private <T> Enumeration<T> getEnumeration(int type) {...} // 获取枚举类对象,type
private <T> Iterator<T> getIterator(int type) {... } // 获取枚举类对象的迭代器
private transient volatile Set<K> keySet; // 键组成的集合
private transient volatile Set<Map.Entry<K,V>> entrySet; // 键值对组成的集合
private transient volatile Collection<V> values; // 值组成的集合
public Set<K> keySet() {...} // 返回键组成的集合
public Set<Map.Entry<K,V>> entrySet() {...} // ...
public Collection<V> values() {...} // ...
public synchronized boolean equals(Object o) {...} // map类型的equals()方法
public synchronized int hashCode() {...} //
// .....
}
2.HashMap(java.util.HashMap)
public class HashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable {
private static final long serialVersionUID = 362498820763181265L;
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // 默认的初始化容量16
static final int MAXIMUM_CAPACITY = 1 << 30; // 最大的容量2^30
static final float DEFAULT_LOAD_FACTOR = 0.75f; // 默认的负载系数0.75f
static final int TREEIFY_THRESHOLD = 8; // 将链表转为红黑树存储的阈值(对应于一个hash值上冲突元素个数)
static final int UNTREEIFY_THRESHOLD = 6; // 将红黑树转为链表存储的阈值
static final int MIN_TREEIFY_CAPACITY = 64; // 使用红黑树时hashmap的最小容量,即只有大于该阈值时,才启用链表->红黑树转换;
static class Node<K,V> implements Map.Entry<K,V> {...} // 存放键值对的节点类型
static final int hash(Object key) {...} // 哈希函数,高16位与低16位异或操作,能够降低冲突
static final int tableSizeFor(int cap) {... } // 根据给定的目标容量,返回可行的2倍数容量
transient Node<K,V>[] table; // 数组存放hashmap的节点键值对数据
transient Set<Map.Entry<K,V>> entrySet; // 键值对集合
transient int size; // 键值对数目
transient int modCount; // 记录hashmap结构改动次数(包括键值对数目变动、内部结构改动)
int threshold; // 阈值
final float loadFactor; // 负载系数
// 构造函数
public HashMap(int initialCapacity, float loadFactor) {...} // 指定初始容量和负载系数
public HashMap(int initialCapacity) {...}
public HashMap() {...}
public HashMap(Map<? extends K, ? extends V> m) {...}
final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {...}
public int size() {...} // 返回键值对数目
public boolean isEmpty() {...} // 判断是否为空
public V get(Object key) {...} // 根据键返回值,不存在返回null
final Node<K,V> getNode(int hash, Object key) {...} // 根据hash值和键查找键值对,不存在则为null
public boolean containsKey(Object key) {...}
public V put(K key, V value) {...} // 往HashMap添加键值对,调用下一个函数
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {...} // 添加节点,若onlyIfAbsent为真,则不改变原有值,否则根据hash和key更新值,不存在则返回null;添加后数量超过阈值则扩容
final Node<K,V>[] resize() {...} // 扩容,(初始化/扩容到2的倍数)
final void treeifyBin(Node<K,V>[] tab, int hash) {...} // 对hashmap中某个hash桶进行树形化存储(满足条件的话)
public void putAll(Map<? extends K, ? extends V> m) {...} // 将map中的键值对添入HashMap
public V remove(Object key) {...} // 根据键删除键值对节点,返回旧值;不存在返回null
final Node<K,V> removeNode(int hash, Object key, Object value, boolean matchValue, boolean movable) {...}
public void clear() {...} // 清空HashMap
public boolean containsValue(Object value) {...} // 判断是否存在对应值的键值对
public Set<K> keySet() {...} // 返回键组成的集合
public Collection<V> values() {...} // 返回值组成的集合
public Set<Map.Entry<K,V>> entrySet() { ...} // 返回键值对组成的集合
//...
}
3.Hashtable和HashMap比较
- Hashtable 为数组+链表结构,HashMap为数组+链表/红黑树结构(优化查询)
- Hashtable 中键、值都不准为null,运行时报空指针异常;HashMap 支持键、值为null
- Hashtable 默认初始容量为11,随后以2*旧容量+1扩容;
HashMap默认初始容量为16,随后以 2*旧容量扩容,扩容后始终为2的倍数;
(关于Hashtable为什么要+1,是因为采用了hash+取模方式寻找桶,扩充后容量为素数/奇数会利于数据的均匀分布) - Hashtable 方法都用synchronized 修饰符进行方法同步,因此线程安全,但同时影响了效率;HashMap非线程安全,但是效率高,并发环境下可以使用ConcurrentHashMap*(位于java. util. concurrent包)*
- 内部迭代器的不同,Hashtable的Enumeration 迭代器非fail-fast(详解见此);而HashMap的Iterator迭代器满足,因此当有其它线程改变了HashMap的结构(增加或者移除元素),会抛出ConcurrentModificationException。