HashMap知识点学习

81 阅读2分钟

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 Snipaste_2022-11-06_16-48-55.png

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

参考资料

HashMap集合介绍+面试题讲解