HashMap介绍
HashMap是一个散列表,它是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,
它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度
。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
HashMap 的实现不是同步的,这意味着它不是线程安全的。它的key、value都可以为null。此外,HashMap中的映射不是有序的。
HashMap 的实例有两个参数影响其性能:capacity 和 loadFactor。
capacity 只是哈希表在创建时的容量。
loadFactor 是哈希表在其容量自动增加之前可以达到多满的一种尺度。
当哈希表中的size超出了loadFactor*capacity,
则要对该哈希表进行 resize 操作(即重建内部数据结构),从而哈希表将具有大约两倍的桶数。
通常,默认loadFactor是 0.75
loadFactor过高虽然减少了空间开销,
但同时也增加了查询成本(在大多数 HashMap 类的操作中,包括 get 和 put 操作,都反映了这一点)。
在设置capacity时应该考虑到映射中所需的条目数及其loadFactor,以便最大限度地减少 rehash 操作次数。
如果capacity大于最大条目数除以loadFactor,则不会发生 resize 操作。
HashMap的构造函数和常用方法
// 默认构造函数。
HashMap()
// 带容量大小的构造函数
HashMap(int capacity)
// 带容量大小和加载因子的构造函数
HashMap(int capacity, float loadFactor)
// 带子Map的构造函数
HashMap(Map<? extends K, ? extends V> map)
源码 (Android API 25)
//默认容量大小,但是在JDK中查看是1 << 4,反正必须是2的幂
static final int DEFAULT_INITIAL_CAPACITY = 4;
//最大容量
static final int MAXIMUM_CAPACITY = 1 << 30;
//默认加载因子
static final float DEFAULT_LOAD_FACTOR = 0.75f;
//在这个map中的键值对数量
transient int size;
//hashmap的阈值,用于判断是否调整hashmap的容量(threshold=capacity * load factor)
int threshold;
//hsshmap结构被修改的次数
transient int modCount;
//hashmap的key-value都存储在table中
transient HashMapEntry<K,V>[] table = (HashMapEntry<K,V>[]) EMPTY_TABLE;
/** *带两个参数的构造方法 */
public HashMap(int initialCapacity, float loadFactor) {
//校验初始容量
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
//对初始容量做判断,最大只能是MAXIMUM_CAPACITY,最小只能是DEFAULT_INITIAL_CAPACITY
if (initialCapacity > MAXIMUM_CAPACITY) {
initialCapacity = MAXIMUM_CAPACITY;
} else if (initialCapacity < DEFAULT_INITIAL_CAPACITY) {
initialCapacity = DEFAULT_INITIAL_CAPACITY;
}
//此时的阈值就是初始容量
threshold = initialCapacity;
init();
}
//根据传入的容量真正的创建HashMapEntry
private void inflateTable(int toSize) {
// Find a power of 2 >= toSize
int capacity = roundUpToPowerOf2(toSize);
// Android-changed: Replace usage of Math.min() here because this method is
// called from the <clinit> of runtime, at which point the native libraries
// needed by Float.* might not be loaded.
float thresholdFloat = capacity * loadFactor;
if (thresholdFloat > MAXIMUM_CAPACITY + 1) {
thresholdFloat = MAXIMUM_CAPACITY + 1;
}
threshold = (int) thresholdFloat;
// 创建Entry数组,用来保存数据
table = new HashMapEntry[capacity];
}
/** *Rehashes the contents of this map into a new array with a * larger capacity. This method is called automatically when the * number of keys in this map reaches its threshold. * *当map中的key达到阈值后自动调用,将map中的内容全部转移到一个newTable中 */
void resize(int newCapacity) {
HashMapEntry[] oldTable = table;
int oldCapacity = oldTable.length;
if (oldCapacity == MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return;
}
HashMapEntry[] newTable = new HashMapEntry[newCapacity];
transfer(newTable);
table = newTable;
threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
}
//将所有的entries转移到newTable中
void transfer(HashMapEntry[] newTable) {
int newCapacity = newTable.length;
for (HashMapEntry<K,V> e : table) {
while(null != e) {
HashMapEntry<K,V> next = e.next;
int i = indexFor(e.hash, newCapacity);
e.next = newTable[i];
newTable[i] = e;
e = next;
}
}
}
//根据key删除
public V remove(Object key) {
Entry<K,V> e = removeEntryForKey(key);
return (e == null ? null : e.getValue());
}
//根据key删除
final Entry<K,V> removeEntryForKey(Object key) {
if (size == 0) {
return null;
}
//根绝key计算出hash值
int hash = (key == null) ? 0 : sun.misc.Hashing.singleWordWangJenkinsHash(key);
int i = indexFor(hash, table.length);
HashMapEntry<K,V> prev = table[i];
HashMapEntry<K,V> e = prev;
while (e != null) {
HashMapEntry<K,V> next = e.next;
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) {
modCount++;
size--;
if (prev == e)
table[i] = next;
else
prev.next = next;
e.recordRemoval(this);
return e;
}
prev = e;
e = next;
}
return e;
}
//获取键值对个数
public int size() {
return size;
}
//判断是否为空
public boolean isEmpty() {
return size == 0;
}
//获取key对应的value
public V get(Object key) {
if (key == null)
return getForNullKey();
Entry<K,V> entry = getEntry(key);
return null == entry ? null : entry.getValue();
}
//hashmap中的key和value都可以为null
private V getForNullKey() {
if (size == 0) {
return null;
}
for (HashMapEntry<K,V> e = table[0]; e != null; e = e.next) {
if (e.key == null)
return e.value;
}
return null;
}
//判断是否包含key
public boolean containsKey(Object key) {
return getEntry(key) != null;
}
//向hashmap中存入键值对
public V put(K key, V value) {
//如果表为空则调用inflateTable创建一个表
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
if (key == null)
return putForNullKey(value);
//计算出hash值
int hash = sun.misc.Hashing.singleWordWangJenkinsHash(key);
int i = indexFor(hash, table.length);
for (HashMapEntry<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++;
//新增一个Entry
addEntry(hash, key, value, i);
return null;
}
//清空整个hashmap
public void clear() {
modCount++;
Arrays.fill(table, null);
size = 0;
}
//判断是否包含某个值
public boolean containsValue(Object value) {
if (value == null)
return containsNullValue();
//创建一个临时的数组,在数组中遍历判断是否包含
HashMapEntry[] tab = table;
for (int i = 0; i < tab.length ; i++)
for (HashMapEntry e = tab[i] ; e != null ; e = e.next)
if (value.equals(e.value))
return true;
return false;
}
//HashMapEntry的实现,包含key、value、next、和hsah值。
static class HashMapEntry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
HashMapEntry<K,V> next;
int hash;
/** * Creates new entry. */
HashMapEntry(int h, K k, V v, HashMapEntry<K,V> n) {
value = v;
next = n;
key = k;
hash = h;
}
public final K getKey() {
return key;
}
public final V getValue() {
return value;
}
public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
//判断两个Entry是否相等,只有当key和value都相等时才返回true
public final boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry e = (Map.Entry)o;
Object k1 = getKey();
Object k2 = e.getKey();
if (k1 == k2 || (k1 != null && k1.equals(k2))) {
Object v1 = getValue();
Object v2 = e.getValue();
if (v1 == v2 || (v1 != null && v1.equals(v2)))
return true;
}
return false;
}
public final int hashCode() {
return Objects.hashCode(getKey()) ^ Objects.hashCode(getValue());
}
public final String toString() {
return getKey() + "=" + getValue();
}
void recordAccess(HashMap<K,V> m) {
}
void recordRemoval(HashMap<K,V> m) {
}
}
//新增一个Entry,有可能导致size超过阈值时调用,然后会创建一个新的hashmap
void addEntry(int hash, K key, V value, int bucketIndex) {
if ((size >= threshold) && (null != table[bucketIndex])) {
//创建一个新的hashmap容量是原来的两倍
resize(2 * table.length);
hash = (null != key) ? sun.misc.Hashing.singleWordWangJenkinsHash(key) : 0;
bucketIndex = indexFor(hash, table.length);
}
createEntry(hash, key, value, bucketIndex);
}
//新增Entry时不会导致size超过阈值时调用此方法
void createEntry(int hash, K key, V value, int bucketIndex) {
HashMapEntry<K,V> e = table[bucketIndex];
table[bucketIndex] = new HashMapEntry<>(hash, key, value, e);
//size加1
size++;
}
HashMap简单使用
HashMap<String, Integer> hashMap = new HashMap<>();
hashMap.put("one", 1);
hashMap.put("two", 2);
hashMap.put("three",3);
System.out.println(hashMap);
//打印结果:
{one=1, two=2, three=3}
hashMap.remove("one");
System.out.println(hashMap);
//打印结果:
{two=2, three=3}
System.out.println(hashMap.containsKey("two"));
//打印结果
true
System.out.println(hashMap.containsValue(3));
//打印结果:
true
HashMap遍历
Iterator iter = map.entrySet().iterator();
while(iter.hasNext()) {
Map.Entry entry = (Map.Entry)iter.next();
// 取key
key = (String)entry.getKey();
// 取value
value = (Integer)entry.getValue();
}