Java Map – HashMap

Java 中的 HashMap 是一个集合类,它实现了Map接口。它用于存储k-v键值对,每个键都映射到Map中的某个值。

键是唯一的,这意味着我们只能在Map中插入一次键 ‘k’,不允许使用重复的 key,虽然一个值’v’可以映射到多个 key。

HashMap 是 JDK 中非常重要的一个类,其源码的阅读价值极大,强烈建议 Java 开发者吃透 HashMap。

java.util.HashMap 类

HashMap 层次结构

HashMap 特性

  • HashMap 不能包含重复的键。
  • HashMap 允许多个null值但只有一个null键。
  • HashMap 是一个无序集合。它不保证元素的任何特定顺序。
  • HashMap不是线程安全的。对 HashMap 的并发修改必须显式同步,或者可以使用Collections.synchronizedMap(hashMap)来获取 HashMap 的同步版本。
  • 只能使用关联的键检索值。
  • HashMap 只存储对象引用,所以原语必须与其对应的包装类一起使用,如int将存储为Integer.
  • HashMap 实现了Cloneable和Serializable接口。

HashMap 内部实现

HashMap 的工作原理是 hash 散列,散列是一种在将任何公式/算法应用于其属性后为任何变量/对象分配唯一码的方法。Java 中的每个对象都有它的哈希码,这样两个相等的对象必须一致地产生相同的哈希码。

HashMap.Entry 类

键值对存储为静态内部类,HashMap.Entry将键和值映射存储为内部类属性,键已标记为final。

static class Node<K,V> implements Map.Entry<K,V> {
    final int hash;
    final K key;
    V value;
    Node<K,V> next;
}

Entry 类的所有实例都存储在一个数组中,声明为’transient Entry[] table’。对于要存储在 HashMap 中的每个键值,使用键的哈希码计算一个哈希值。该哈希值用于计算数组中用于存储 Entry 对象的索引。

在碰撞的情况下,其中多个键被映射到单个索引位置,形成一个链表来存储所有应该进入单个数组索引位置的键值对。

通过键检索值时,使用键的哈希码找到第一个索引位置,然后在链表中迭代所有元素,并通过使用它的equals()方法识别正确的键来找到正确的值对象。

Java HashMap 示例

下面将从最常见的增加、查询、删除、遍历键值对来演示如何使用 HashMap。

添加键值对 put()

public static void main(String[] args) {
    Map<Integer, String> nameMap = new HashMap<>();
    nameMap.put(1, "Alice");
    nameMap.put(2, "Bob");
    nameMap.put(3, "Carl");

    System.out.println(nameMap);
}
{1=Alice, 2=Bob, 3=Carl}
执行结果

获取值 get()

public static void main(String[] args) {
    Map<Integer, String> nameMap = new HashMap<>();
    nameMap.put(1, "Alice");
    nameMap.put(2, "Bob");
    nameMap.put(3, "Carl");

    String name = nameMap.get(2);
    System.out.println(name);
}
Bob
执行结果

通过key删除value remove()

public static void main(String[] args) {
    Map<Integer, String> nameMap = new HashMap<>();
    nameMap.put(1, "Alice");
    nameMap.put(2, "Bob");
    nameMap.put(3, "Carl");
    System.out.println(nameMap);
    nameMap.remove(2);
    System.out.println(nameMap);
}
{1=Alice, 2=Bob, 3=Carl}
{1=Alice, 3=Carl}
执行结果

遍历所有元素

public static void main(String[] args) {
    Map<Integer, String> nameMap = new HashMap<>();
    nameMap.put(1, "Alice");
    nameMap.put(2, "Bob");
    nameMap.put(3, "Carl");

    Iterator<Integer> iterator = nameMap.keySet().iterator();
    while (iterator.hasNext()){
        Integer key = iterator.next();
        String value = nameMap.get(key);
        System.out.println("key - > " + key + ", value - > " + value);
    }

    for (Integer key : nameMap.keySet()) {
        String value = nameMap.get(key);
        System.out.println("key1 - > " + key + ", value1 - > " + value);
    }

    Iterator<Map.Entry<Integer, String>> iterator1 = nameMap.entrySet().iterator();
    while (iterator1.hasNext()){
        Map.Entry<Integer, String> entry = iterator1.next();
        Integer key = entry.getKey();
        String value = entry.getValue();
        System.out.println("key2 - > " + key + ", value2 - > " + value);
    }

    for (Map.Entry<Integer, String> entry : nameMap.entrySet()) {
        Integer key = entry.getKey();
        String value = entry.getValue();
        System.out.println("key3 - > " + key + ", value3 - > " + value);
    }
}
key – > 1, value – > Alice
key – > 2, value – > Bob
key – > 3, value – > Carl
key1 – > 1, value1 – > Alice
key1 – > 2, value1 – > Bob
key1 – > 3, value1 – > Carl
key2 – > 1, value2 – > Alice
key2 – > 2, value2 – > Bob
key2 – > 3, value2 – > Carl
key3 – > 1, value3 – > Alice
key3 – > 2, value3 – > Bob
key3 – > 3, value3 – > Carl
执行结果

HashMap 重要方法

方法签名 释义
void clear() 从 HashMap 中删除所有键值对。
Object clone() 返回指定 HashMap 的浅拷贝。
boolean containsKey(Object key) 返回true或false基于是否在HashMap中找到指定的键。
boolean containsValue(Object Value) 类似于 containsKey() 方法,它查找指定的值而不是键。
Object get(Object key) 返回 HashMap 中指定键的值。
boolean isEmpty() 检查HashMap是否为空。
Set keySet() 返回存储在 HashMap 中的所有键的Set。
Object put(Key k, Value v) 将键值对插入到 HashMap 中。
int size() 返回映射的大小,它等于存储在 HashMap 中的键值对的数量。
Collection values() 返回HashMap中所有值的集合。
Value remove(Object key) 删除指定键的键值对。
void putAll(Map m) 将HashMap的所有元素复制到另一个指定的HashMap。
标签: