跳转至

在 Java 中创建 HashMap

简介

在 Java 编程中,HashMap 是一个非常重要且常用的数据结构。它实现了 Map 接口,用于存储键值对(key-value pairs),并且允许 null 键和 null 值。HashMap 基于哈希表(hash table)实现,这使得它在查找、插入和删除操作上具有高效性,在许多实际应用场景中发挥着关键作用。本文将深入探讨在 Java 中创建 HashMap 的基础概念、使用方法、常见实践以及最佳实践。

目录

  1. 基础概念
  2. 使用方法
    • 创建空的 HashMap
    • 创建带有初始容量的 HashMap
    • 创建带有初始容量和加载因子的 HashMap
    • 从另一个 Map 创建 HashMap
  3. 常见实践
    • 添加键值对
    • 获取值
    • 修改值
    • 删除键值对
    • 遍历 HashMap
  4. 最佳实践
    • 选择合适的初始容量
    • 了解加载因子
    • 正确处理键的哈希值
  5. 小结
  6. 参考资料

基础概念

HashMap 是 Java 集合框架的一部分,它基于哈希表数据结构。哈希表是一种数据结构,通过计算键的哈希值(hash code)来确定存储位置,从而实现快速查找。每个键值对在哈希表中被称为一个 entry

哈希值是一个整数,HashMap 使用这个值来决定将键值对存储在哈希表的哪个桶(bucket)中。当两个不同的键计算出相同的哈希值时,就会发生哈希冲突(hash collision)。HashMap 使用链地址法(separate chaining)来解决哈希冲突,即把冲突的键值对存储在同一个桶中的链表中。

使用方法

创建空的 HashMap

import java.util.HashMap;
import java.util.Map;

public class HashMapExample {
    public static void main(String[] args) {
        // 创建一个空的 HashMap
        Map<String, Integer> hashMap = new HashMap<>();
    }
}

在上述代码中,我们使用无参构造函数创建了一个空的 HashMap,键的类型为 String,值的类型为 Integer

创建带有初始容量的 HashMap

import java.util.HashMap;
import java.util.Map;

public class HashMapExample {
    public static void main(String[] args) {
        // 创建一个带有初始容量为 16 的 HashMap
        Map<String, Integer> hashMap = new HashMap<>(16);
    }
}

通过传递一个整数参数作为初始容量,可以创建一个具有指定初始容量的 HashMap。初始容量表示哈希表中桶的数量。

创建带有初始容量和加载因子的 HashMap

import java.util.HashMap;
import java.util.Map;

public class HashMapExample {
    public static void main(String[] args) {
        // 创建一个带有初始容量为 16,加载因子为 0.75 的 HashMap
        Map<String, Integer> hashMap = new HashMap<>(16, 0.75f);
    }
}

加载因子(load factor)是一个阈值,当哈希表中的键值对数量达到 容量 * 加载因子 时,哈希表会自动扩容(resize)。默认的加载因子是 0.75。

从另一个 Map 创建 HashMap

import java.util.HashMap;
import java.util.Map;

public class HashMapExample {
    public static void main(String[] args) {
        // 创建一个源 Map
        Map<String, Integer> sourceMap = new HashMap<>();
        sourceMap.put("one", 1);
        sourceMap.put("two", 2);

        // 从源 Map 创建一个新的 HashMap
        Map<String, Integer> hashMap = new HashMap<>(sourceMap);
    }
}

通过传递一个现有的 Map 作为参数,可以创建一个包含该 Map 所有键值对的 HashMap

常见实践

添加键值对

import java.util.HashMap;
import java.util.Map;

public class HashMapExample {
    public static void main(String[] args) {
        Map<String, Integer> hashMap = new HashMap<>();
        // 添加键值对
        hashMap.put("one", 1);
        hashMap.put("two", 2);
        hashMap.put("three", 3);
    }
}

使用 put 方法可以向 HashMap 中添加键值对。如果键已经存在,则会覆盖其对应的值。

获取值

import java.util.HashMap;
import java.util.Map;

public class HashMapExample {
    public static void main(String[] args) {
        Map<String, Integer> hashMap = new HashMap<>();
        hashMap.put("one", 1);
        hashMap.put("two", 2);

        // 获取值
        Integer value = hashMap.get("one");
        System.out.println("Value for key 'one': " + value);
    }
}

使用 get 方法可以根据键获取对应的值。如果键不存在,会返回 null

修改值

import java.util.HashMap;
import java.util.Map;

public class HashMapExample {
    public static void main(String[] args) {
        Map<String, Integer> hashMap = new HashMap<>();
        hashMap.put("one", 1);

        // 修改值
        hashMap.put("one", 100);
        Integer newValue = hashMap.get("one");
        System.out.println("New value for key 'one': " + newValue);
    }
}

再次使用 put 方法,将相同的键作为参数传入,即可修改其对应的值。

删除键值对

import java.util.HashMap;
import java.util.Map;

public class HashMapExample {
    public static void main(String[] args) {
        Map<String, Integer> hashMap = new HashMap<>();
        hashMap.put("one", 1);

        // 删除键值对
        hashMap.remove("one");
        Integer removedValue = hashMap.get("one");
        System.out.println("Value after removing 'one': " + removedValue);
    }
}

使用 remove 方法可以根据键删除对应的键值对。

遍历 HashMap

import java.util.HashMap;
import java.util.Map;

public class HashMapExample {
    public static void main(String[] args) {
        Map<String, Integer> hashMap = new HashMap<>();
        hashMap.put("one", 1);
        hashMap.put("two", 2);
        hashMap.put("three", 3);

        // 遍历键值对
        for (Map.Entry<String, Integer> entry : hashMap.entrySet()) {
            System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());
        }

        // 遍历键
        for (String key : hashMap.keySet()) {
            System.out.println("Key: " + key);
        }

        // 遍历值
        for (Integer value : hashMap.values()) {
            System.out.println("Value: " + value);
        }
    }
}

可以使用 entrySetkeySetvalues 方法分别遍历键值对、键和值。

最佳实践

选择合适的初始容量

如果预先知道 HashMap 中大致会存储多少个键值对,选择合适的初始容量可以减少哈希表的扩容次数,提高性能。一般来说,初始容量应该设置为大于预期元素数量除以加载因子的最小 2 的幂次方。例如,如果预期存储 100 个元素,加载因子为 0.75,则初始容量应设置为 (int) Math.ceil(100 / 0.75) = 134,最接近的 2 的幂次方是 128 或 256,为了避免频繁扩容,可选择 256。

了解加载因子

加载因子的选择会影响哈希表的性能。较小的加载因子会减少哈希冲突的概率,但会导致哈希表占用更多的空间;较大的加载因子会增加哈希冲突的可能性,但会减少哈希表的空间占用。默认的加载因子 0.75 是一个在空间和性能之间取得较好平衡的值。在实际应用中,需要根据具体需求来调整加载因子。

正确处理键的哈希值

为了确保 HashMap 的性能,键的 hashCode 方法应该正确实现。良好的 hashCode 实现应该尽量使不同的键产生不同的哈希值,减少哈希冲突。同时,equals 方法也需要正确实现,并且要与 hashCode 方法保持一致。即如果两个对象通过 equals 方法比较相等,那么它们的 hashCode 方法返回值必须相同。

小结

本文详细介绍了在 Java 中创建 HashMap 的各种方法,以及常见的操作实践和最佳实践。通过了解 HashMap 的基础概念、合理选择初始容量和加载因子、正确处理键的哈希值等,可以在实际编程中更高效地使用 HashMap,提升程序的性能和稳定性。

参考资料