跳转至

Java HashMap 示例详解

简介

在 Java 编程中,HashMap 是一个极为常用的数据结构。它实现了 Map 接口,用于存储键值对(key-value pairs),能够提供快速的查找、插入和删除操作。通过学习 Java HashMap 的基础概念、使用方法、常见实践以及最佳实践,开发者可以更高效地利用这一强大工具来解决各种实际问题。

目录

  1. 基础概念
    • 什么是 HashMap
    • 哈希原理
  2. 使用方法
    • 创建 HashMap
    • 添加键值对
    • 获取值
    • 修改值
    • 删除键值对
    • 遍历 HashMap
  3. 常见实践
    • 存储自定义对象
    • 处理空键和空值
  4. 最佳实践
    • 选择合适的初始容量
    • 注意哈希冲突
    • 避免在迭代时修改
  5. 小结

基础概念

什么是 HashMap

HashMap 是 Java 集合框架中的一部分,它允许我们存储和检索与特定键相关联的值。每个键值对在 HashMap 中被称为一个 entryHashMap 不保证元素的顺序,并且允许一个 null 键和多个 null 值。

哈希原理

HashMap 使用哈希表来存储数据。哈希表是一种基于哈希函数的数据结构,它通过将键的哈希值映射到数组的索引位置来快速定位对应的值。当两个不同的键计算出相同的哈希值时,就会发生哈希冲突。HashMap 使用链地址法(separate chaining)来解决哈希冲突,即当发生冲突时,会将冲突的元素存储在链表或红黑树(JDK 8 引入了红黑树优化)中。

使用方法

创建 HashMap

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

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

        // 创建一个带有初始容量的 HashMap
        HashMap<String, Integer> hashMapWithCapacity = new HashMap<>(16);

        // 创建一个基于另一个 Map 的 HashMap
        Map<String, Integer> anotherMap = new HashMap<>();
        anotherMap.put("one", 1);
        HashMap<String, Integer> hashMapFromMap = new HashMap<>(anotherMap);
    }
}

添加键值对

import java.util.HashMap;

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

        // 如果键已存在,put 方法会覆盖旧的值
        hashMap.put("one", 11);
    }
}

获取值

import java.util.HashMap;

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

        Integer value = hashMap.get("one");
        if (value!= null) {
            System.out.println("The value for key 'one' is: " + value);
        }

        // getOrDefault 方法在键不存在时返回默认值
        Integer defaultValue = hashMap.getOrDefault("three", 0);
        System.out.println("The default value for key 'three' is: " + defaultValue);
    }
}

修改值

import java.util.HashMap;

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

        // 直接使用 put 方法修改值
        hashMap.put("one", 11);

        // compute 方法可以根据键和旧值计算新值
        hashMap.compute("one", (key, oldValue) -> oldValue == null? 0 : oldValue + 1);
    }
}

删除键值对

import java.util.HashMap;

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

        // remove 方法根据键删除键值对
        hashMap.remove("one");

        // remove 方法也可以根据键和值来删除
        hashMap.remove("two", 2);
    }
}

遍历 HashMap

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

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

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

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

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

        // 使用 Lambda 表达式遍历
        hashMap.forEach((key, value) -> System.out.println("Key: " + key + ", Value: " + value));
    }
}

常见实践

存储自定义对象

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

class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + age;
        result = prime * result + ((name == null)? 0 : name.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass()!= obj.getClass())
            return false;
        Person other = (Person) obj;
        if (age!= other.age)
            return false;
        if (name == null) {
            if (other.name!= null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }
}

public class HashMapCustomObjectExample {
    public static void main(String[] args) {
        HashMap<Person, String> hashMap = new HashMap<>();
        Person person1 = new Person("Alice", 25);
        Person person2 = new Person("Bob", 30);

        hashMap.put(person1, "Person 1");
        hashMap.put(person2, "Person 2");

        for (Map.Entry<Person, String> entry : hashMap.entrySet()) {
            System.out.println("Key: " + entry.getKey().name + ", Value: " + entry.getValue());
        }
    }
}

处理空键和空值

import java.util.HashMap;

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

        Integer valueForKey = hashMap.get(null);
        Integer valueForNull = hashMap.get("key");

        System.out.println("Value for null key: " + valueForKey);
        System.out.println("Value for key with null value: " + valueForNull);
    }
}

最佳实践

选择合适的初始容量

如果预先知道要存储的元素数量,选择合适的初始容量可以减少哈希表的扩容次数,提高性能。可以使用公式 initialCapacity = (expectedSize / loadFactor) + 1 来计算初始容量。例如:

HashMap<String, Integer> hashMap = new HashMap<>(100);

注意哈希冲突

尽量确保自定义对象的 hashCode 方法能够均匀地分布哈希值,减少哈希冲突。同时,避免使用可变对象作为键,因为对象状态的改变可能会导致哈希值的改变,从而影响 HashMap 的正常操作。

避免在迭代时修改

在遍历 HashMap 时,不要直接调用 removeput 方法修改 HashMap 的结构,这可能会导致 ConcurrentModificationException。可以使用迭代器的 remove 方法或者创建一个新的 HashMap 来进行修改。例如:

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

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

        Iterator<Map.Entry<String, Integer>> iterator = hashMap.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, Integer> entry = iterator.next();
            if ("one".equals(entry.getKey())) {
                iterator.remove();
            }
        }
    }
}

小结

Java HashMap 是一个功能强大且常用的数据结构,通过理解其基础概念、掌握使用方法、了解常见实践以及遵循最佳实践,开发者可以在实际项目中高效地使用它。无论是简单的键值对存储,还是复杂的业务逻辑处理,HashMap 都能发挥重要作用。希望本文能帮助读者更深入地理解并运用 Java HashMap