HashMap可以存储键值对,是数据存储时经常使用到的一个集合类。经常使用也就要好好研究下喽。HashMap类设计巧妙,水平有限,只能简单分析下,尝做一帖记录。
源码分析:
实现了Map,Cloneable,Serializable接口:
public class HashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
HashMap中的关键属性:
static final int DEFAULT_INITIAL_CAPACITY = 16; //初始容量大小
transient Entry[] table;//存储元素的实体数组
transient int size;//存放元素的个数
int threshold; //临界值 当实际大小超过临界值时,会进行扩容threshold = 加载因子*容量
final float loadFactor; //加载因子
transient int modCount;//被修改的次数
loadFactor加载因子是表示Hash表中元素的填满的程度
构造方法:
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);
// Find a power of 2 >= initialCapacity
int capacity = 1; //初始容量
while (capacity < initialCapacity) //确保容量为2的n次幂,使capacity为大于initialCapacity的最小的2的n次幂
capacity <<= 1;
this.loadFactor = loadFactor;
threshold = (int)(capacity * loadFactor);
table = new Entry[capacity];
init();
}
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR;
threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
table = new Entry[DEFAULT_INITIAL_CAPACITY];
init();
}
HashMap的get,put 操作:
public V get(Object key) {
if (key == null)
return getForNullKey();
int hash = hash(key.hashCode());
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.equals(k)))
return e.value;
}
return null;
}
public V put(K key, V value) {
// 若“key为null”,则将该键值对添加到table[0]中。
if (key == null)
return putForNullKey(value);
//若“key不为null”,则计算该key的哈希值,然后将其添加到该哈希值对应的链表中。
int hash = hash(key.hashCode());
//搜索指定hash值在对应table中的索引
int i = indexFor(hash, table.length);
//循环遍历Entry数组,若“该key”对应的键值对已经存在,则用新的value取代旧的value。然后退出!
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;
}
再上个HashMap的小例子:
进行了HashMap的简单尝试
package javaCollection;
import java.util.Map;
import java.util.HashMap;
import java.util.Hashtable;
public class HashMapDemo {
/**
* 实现哈希表的数据结构
*
* Hashtable 不可以存入null 键和null 值;但是HashMap 可以存入null 键和null 值。
hashtable 是线程安全Map 实现;但HashMap 是线程不安全的实现类。所以HashMap 性能比较高点。
* @param args
*/
public static void main(String[] args)
{
Map<String,String> hash = new HashMap<String, String>() ;
// 给HashMap 集合存入null键或者null值
hash.put("111", null) ;
hash.put(null, "1112") ;
// 取出的结果为 1112
System.out.println(hash.get(null));
Map<String,String> table = new Hashtable<String, String>() ;
// 下面的代码会出现运行异常:NullPointerException
// table.put("111", null) ; HashTable下不能插入null的值
// table.put(null, "1112") ; HashTable不能插入null的键
table.put("pi","11" );
System.out.println(table.get("pi"));
}
}
记得上次碰到HashMap的扩容实现问题:
总的而言:
当添加元素后,数组的长度超过阈值继而进行扩容:
1.初始容量为16,阈值12
2.计算新的数组长度并初始化
新的长度为原来长度*2
新的阈值为新的长度*loadFactor
3.将原来的数据迁移到新的数组