HashMap
JDK1.7:数组+链表
JDK1.8:数组+链表+红黑树
类:
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {...}
初始化大小16Byte:code%lenth=code^(length-1)
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
链表大于8(泊松分布)并且数组长度大于64Byte,结构转换为红黑树:
static final int TREEIFY_THRESHOLD = 8;
static final int MIN_TREEIFY_CAPACITY = 64;
容量初始阈值(大于8扩容):
int threshold;
红黑树转链表:
static final int UNTREEIFY_THRESHOLD = 6;
存放KV值:
transient Node<K,V>[] table;
KV实时数据大小,不是table大小:
transient int size;
默认负载因子(假如真正在length*0.75就开始扩容):
static final float DEFAULT_LOAD_FACTOR = 0.75f;
加载因子:=size/length
final float loadFactor;
put
//.put方法
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
//对K进行hash操作
static final int hash(Object key) {
int h; //返回散列值也就是hashcode。假设随便生成的一个值
// hashCode值的高位变化很大,而地位变化很小或者没有变化,
// 那么如果直接和数组长度进行&运算会很容易造成计算的结果一样的,导致hash碰撞。
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
//
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
... //hash值进行按位与得到数组下标(索引)
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
...
}
++modCount; //数组改变就+1
if (++size > threshold) //当前大小和阈值做比较
resize();
afterNodeInsertion(evict);
return null;
}
此处是针对链表?
JDK1.7数组扩容:重新计算hash值
JDK1.8:扩容之后的索引位置要么是原来索引,要么是原来索引+旧数组的容量
计算新索引的高位:(e.hash & oldCap) == 0
final Node<K,V>[] resize() {
...
do {
next = e.next;
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
else {
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;
}
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
...
}
remove
get
遍历
public class HashMapDemo {
public static void main(String[] args) {
HashMap<String,Integer> map = new HashMap<>();
map.put("zhangsan", 18);
map.put("wangwu", 28);
map.put("zhaoliu", 20);
System.out.println(map); //{zhaoliu=20, zhangsan=18, wangwu=28}
method(map);
}
/**
* 分别遍历key和value的值
* @param map
*/
private static void method(HashMap<String, Integer> map) {
//获取所有的key
Set<String> keys = map.keySet();
for (String key : keys) {
System.out.println(key);
}
//获取所有的value
Collection<Integer> values = map.values();
for (Integer value : values) {
System.out.println(value);
}
}
}
zhaoliu
zhangsan
wangwu
20
18
28
/**
* 使用迭代器
* @param map
*/
private static void method1(HashMap<String, Integer> map) {
Set<Map.Entry<String, Integer>> entries = map.entrySet();
for(Iterator<Map.Entry<String, Integer>> it = entries.iterator();it.hasNext();) {
Map.Entry<String, Integer> entry = it.next();
System.out.println(entry.getKey()+"->"+entry.getValue());
}
}
zhaoliu->20
zhangsan->18
wangwu->28
/**
* 通过get方法,不建议使用,效率低
* @param map
*/
private static void method2(HashMap<String, Integer> map) {
Set<String> keys = map.keySet();
for (String key : keys) {
Integer value = map.get(key);
System.out.println(key+"->"+value);
}
}
zhaoliu->20
zhangsan->18
wangwu->28
/**
jdk8以后使用Map接口中的默认方法:
default void forEach(BiConsumer<? super K, ? super V> action )
对此映射中的每个条目执行给定的操作,直到所有条目都被处理或操作引发异常。参数:
BiConsumer消费接口:
抽象方法:void accept (T t, U u)对给定的参数执行此操作。
参数:
t key
u value
* @param map
*/
private static void method3(HashMap<String, Integer> map) {
map.forEach((key,value) -> {
System.out.println(key+"->"+value);
});
}