Java集合之HashMap、LinkedHashMap、HashTable
讨论集合关注的问题:
- 底层数据结构
- 增删改查方式
- 初始容量,扩容方式,扩容时机
- 线程安全与否
- 是否允许空,是否允许重复,是否有序
我们都知道Collection接口派生出三大类的子接口List,Set和Queue。今天来看看另一个派系,没错就是Map。先来复习一下这张集合关系网(不全):
可以看到,Map作为Collection的“生产者”,在另一种形式也就是通常说的非线性结构集合——键值对,具有很好的拓展空间。这个系列主要用来存储非线性的集合数据类型。常用的有HashMap,HashTable,TreeMap等。下面详细讨论其特点和源码分析。一般在开发中,我们常用Map接口来动态使用这些类。
HashMap
作为最常用的Map型数据结构,HashMap采用了Hash的方式来存储数据,能够快速的获取存储位置的数据。
来看源码,HashMap继承自AbstractMap抽象类,同时实现了Map,Cloneable和Serializable接口,说明HashMap可以实现克隆和序列化等操作。其内部实现了Node<K,V>的内部类,来存放数据。
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
public final K getKey() {
return key; }
public final V getValue() {
return value; }
public final String toString() {
return key + "=" + value; }
public final int hashCode() {
return Objects.hashCode(key) ^ Objects.hashCode(value);
}
public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
public final boolean equals(Object o) {
if (o == this)
return true;
if (o instanceof Map.Entry) {
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
if (Objects.equals(key, e.getKey()) &&
Objects.equals(value, e.getValue()))
return true;
}
return false;
}
}
HashMap可以指定容量大小和一个加载因子进行初始化,默认的容量大小为16,加载因子为0.75;这里加载因子loadFactor的概念是新出现的,表示这个散列表中使用的程度,当超过容量的这个百分比值就会进行扩容。
HashMap的内部使用一个table数组进行键的维护,这个表也就是hash的桶(即初始容量大小),不同的位置存放着不同的hash值的数据。其中,多个hash值相同的元素,将会使用一个链表的形式连接在一起,使用偏移量作为标记。
HashMap中的Hash函数如下:
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()