Java HashMap

HashMap 是 Java 集合框架中使用最频繁的 Map 实现。HashMap 通过 key-value 键值对存储数据,并通过一系列的特有 API 添加、检索、删除数据。

键值对存储在所谓的存储桶中,这些存储桶共同构成了所谓的表,key 是一个内部数组,value 则是一个链表。

增加元素

为了在HashMap中存储一个值,我们调用 put()方法,它接受两个参数:

public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

该方法内部调用了 putVal() 函数,参数含义:

  • hash – key 的哈希值
  • key – key 键
  • value – value 值
  • onlyIfAbsent – 当要插入的key已存在时是否替换value,false则替换,true则不替换。
  • evict – 在HashMap中无作用,只在LinkedHashMap需要。

其中的 hash() 方法:

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

hash() 方法调用了传入 key 的 hashCode() 方法计算原始的哈希值,这也是该方法中的唯一变化量。

不过,我们还看到了 ^ (h >>> 16) 这样一段计算逻辑,按照官方的表述,可以用下面的图示来说明:

将h无符号右移16位相当于将高区16位移动到了低区的16位,再与原hashcode做异或运算,高区的16位与原hashcode相比没有发生变化,低区的16位发生了变化,这样就将高低位二进制特征混合起来,可以进一步降低 hash 值的重复概率。

hash() 方法对入参为 null 的元素分配了哈希值 0,意味着 null 可以作为 HashMap 的一个元素。

实际上,不但 key 可以为 null, value 也可以为 null:

Map<String, String> map = new HashMap<>();
map.put(null, null);

有几点需要注意:

  1. put 方法有返回值。当我们使用之前已经用过的键来存储值时,它会返回与该键关联的旧值:否则返回 null;
  2. 所有 Java 集合框架接口都继承自 Collection接口,但 Map 没有。原因是 Map 不像其他集合那样完全存储单个元素,而是键值对的集合。所以 Collection 接口的方法如 add、toArray 方法对Map没有意义。

获取元素

获取 HashMap 中的元素,我们需要知道 key 是什么,然后调用 get() 方法。

String val = map.get("key");

get() 方法签名:

public V get(Object key) {
    Node<K,V> e;
    return (e = getNode(hash(key), key)) == null ? null : e.value;
}

其内部也先调用了 hash() 方法获取 key 的哈希值。这个哈希值就是存储桶的位置,即数组索引下标。

当返回值为 null 时,可能意味着键对象未与哈希映射中的任何值关联,或者最开始我们就存放了一个 null 值。

元素集合

HashMap 提供了几个方法可以将键或者直接提取出来。

获取所有的 key:

Set<String> keys = map.keySet();

获取所有的 value:

Collection<String> values = map.values();

获取所有的 key-value:

Set<Entry<String, String>> entries = map.entrySet();

转载请注明出处:码谱记录 » Java HashMap
标签: