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(); } .. }