1. HashMap内存储的元素是Entry,并且Entry是按照链表的形式来存储的。
transient Entry<K,V>[] table; // 用数组来存储,它的原理是每个数组的元素都是一个链表头
Entry的定义如下:
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next;
int hash;
}
2. get(Object key)方法解读
public V get(Object key) {
// 对key进行判空
if (key == null)
return getForNullKey();
// 根据key找Entry
Entry<K,V> entry = getEntry(key);
return null == entry ? null : entry.getValue();
}
final Entry<K,V> getEntry(Object key) {
//计算hash值
int hash = (key == null) ? 0 : hash(key);
// 根据hash值来查找在数组中的下标位置,然后沿着链表进行查找
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;
}
3. put(key,value)方法解读
public V put(K key, V value) {
// 对key进行判空处理
if (key == null)
return putForNullKey(value);
// 计算hash值
int hash = hash(key);
// 查找在数据中的位置,然后沿着链表查找 ,如果有相同的值就覆盖,没有就加一个entry
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;
}
4.常见的参数设置
默认大小是16
static final int DEFAULT_INITIAL_CAPACITY = 16;
当容量超过16时,就会扩容,然后会重新计算hash值,所以尽量在初始化的时候指定大小。
5. hashmap vs hashtable
1)hashmap 是允许存储空值的,然hashtable是不允许的;
2)初始化的大小不一样,hashmap是16,hashtable是11;
3)hash的计算公式是不一样的;
4) hashtable是线程安全的,而hashmap不是线程安全的。hashtable是怎么来保证线程安
全的呢?(有些人看了一些面试宝典之类的,只是记住了这个结论)
// 使用synchronized 来实现线程安全的
public synchronized V put(K key, V value) {
// Make sure the value is not null(不能为空的)
if (value == null) {
throw new NullPointerException();
}
..
}