玖叶教程网

前端编程开发入门

Java路径-36-Java的HashMap

1 HashMap的概念

HashMap集合是Map接口的一个实现类,它用于存储键值映射关系,该集合的键和值允许为空,但键不能重复,且集合中的元素是无序的。

特点

HashMap底层是由哈希表结构组成的,其实就是“数组+链表”的组合体,数组是HashMap的主体结构,链表则主要是为了解决哈希值冲突而存在的分支结构。正因为这样特殊的存储结构,HashMap集合对于元素的增、删、改、查操作表现出的效率都比较高。

结构

在java1.8以后采用数组+链表+红黑树的形势来进行存储,通过散列映射来存储键值对,如下图:



  • 在初始化时将会给定默认容量为16
  • 对key的hashcode进行一个取模操作得到数组下标
  • 数组存储的是一个单链表
  • 数组下标相同将会被放在同一个链表中进行存储
  • 元素是无序排列的
  • 链表超过一定长度(TREEIFY_THRESHOLD=8)会转化为红黑树
  • 红黑树在满足一定条件会再次退回链表

2 HashMap的定义

HashMap的构造函数

HashMap共有4个构造函数,如下:

// 默认构造函数。
HashMap()

// 指定“容量大小”的构造函数
HashMap(int capacity)

// 指定“容量大小”和“加载因子”的构造函数
HashMap(int capacity, float loadFactor)

// 包含“子Map”的构造函数
HashMap(Map<? extends K, ? extends V> map)

3 HashMap的使用

3.1 HashMap的基本用法

创建HashMap对象 要创建一个HashMap对象,您可以使用如下的方式:

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

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

添加键值对 您可以使用put方法来向HashMap中添加键值对:

hashMap.put("apple", 1);
hashMap.put("banana", 2);
hashMap.put("cherry", 3);

获取值 要从HashMap中获取值,可以使用get方法,并传入键:

int value = hashMap.get("banana"); // 获取键"banana"对应的值,此时value为2

删除键值对 要删除HashMap中的键值对,可以使用remove方法:

hashMap.remove("apple"); // 删除键"apple"对应的键值对

遍历HashMap 遍历HashMap可以使用不同的方法,最常见的是使用forEach方法:

hashMap.forEach((key, value) -> {
    System.out.println(key + ": " + value);
});

3.2 HashMap的高级用法

处理碰撞 HashMap在处理哈希碰撞(即两个不同的键映射到了同一个哈希桶中)时,使用了链表和红黑树结构来存储键值对。这使得HashMap在大多数情况下都能提供O(1)的性能。

容量和负载因子 HashMap有两个重要的参数,分别是容量(capacity)和负载因子(load factor)。容量是哈希表中桶的数量,而负载因子是桶的填充程度。当HashMap中的元素数量超过容量与负载因子的乘积时,哈希表会进行扩容,以保持性能。

Map<String, Integer> hashMap = new HashMap<>(16, 0.75f);

遍历键集合或值集合 除了使用forEach方法遍历键值对外,您还可以使用keySet和values方法来分别获取键的集合和值的集合,并进行遍历:

Set<String> keys = hashMap.keySet(); // 获取所有键的集合
Collection<Integer> values = hashMap.values(); // 获取所有值的集合

for (String key : keys) {
    System.out.println(key);
}

for (int value : values) {
    System.out.println(value);
}

替代默认值 当从HashMap中获取值时,如果键不存在,通常会返回null。如果您希望在键不存在时返回一个默认值,可以使用getOrDefault方法:

int value = hashMap.getOrDefault("orange", 0); // 如果键"orange"不存在,返回默认值0

合并操作 您可以使用merge方法来合并两个HashMap:

Map<String, Integer> anotherMap = new HashMap<>();
anotherMap.put("apple", 5);
anotherMap.put("banana", 6);

anotherMap.forEach((key, value) -> {
    hashMap.merge(key, value, (oldValue, newValue) -> oldValue + newValue);
});

这将合并两个HashMap,如果键相同,则将值相加。

3.3 更多操作

当涉及到HashMap的更多操作时,有一些重要的概念和方法可以帮助您更灵活地处理数据。以下是一些HashMap的更多操作:

  1. 获取HashMap的大小 要获取HashMap中键值对的数量,可以使用size方法:
int size = hashMap.size();

这将返回HashMap中键值对的数量。

  1. 判断HashMap是否为空 您可以使用isEmpty方法来检查HashMap是否为空:
boolean isEmpty = hashMap.isEmpty();

如果HashMap为空,将返回true,否则返回false。

  1. 替换值 如果要替换HashMap中的值,可以使用replace方法:
hashMap.replace("apple", 4); // 将键"apple"对应的值替换为4
  1. 清空HashMap 要清空HashMap中的所有键值对,可以使用clear方法:
hashMap.clear(); // 清空HashMap
  1. 判断是否包含键或值 您可以使用containsKey方法来检查HashMap是否包含特定键:
boolean containsKey = hashMap.containsKey("apple"); // 检查是否包含键"apple"

同样地,您可以使用containsValue方法来检查HashMap是否包含特定值:

boolean containsValue = hashMap.containsValue(2); // 检查是否包含值2
  1. 复制HashMap 如果需要创建一个与现有HashMap相同的副本,可以使用clone方法:
Map<String, Integer> copyHashMap = new HashMap<>(hashMap);

这将创建一个与hashMap相同的新HashMap。

  1. 获取键值对的集合 除了使用keySet和values方法获取键集合和值集合外,您还可以使用entrySet方法来获取键值对的集合:
Set<Map.Entry<String, Integer>> entrySet = hashMap.entrySet();

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

这将允许您迭代访问HashMap中的键值对。

  1. 同步HashMap 如果需要在多个线程之间共享HashMap,并且希望确保线程安全性,可以使用Collections.synchronizedMap方法创建同步的HashMap:
Map<String, Integer> synchronizedHashMap = Collections.synchronizedMap(hashMap);

这将返回一个线程安全的HashMap。

  1. 获取键或值的集合视图 如果需要获取HashMap中键或值的集合视图,可以使用keySet和values方法。这些集合视图是与原始HashMap关联的,对它们的更改将影响原始HashMap。
  2. 计算HashMap的哈希码 要计算HashMap的哈希码,可以使用hashCode方法:
int hashCode = hashMap.hashCode();

这将返回HashMap的哈希码值。

  1. 处理默认值 如果要从HashMap中获取值,如果键不存在,不仅返回默认值,还可以在键不存在时执行某个操作。您可以使用computeIfAbsent方法来实现这一点:
hashMap.computeIfAbsent("orange", key -> {
    // 在键"orange"不存在时,执行此操作并返回默认值0
    return 0;
});

这将允许您在获取值的同时进行计算或其他操作。

4 注意事项

当使用HashMap时,有一些注意事项需要考虑,以确保您的代码正确且高效地运行。以下是一些重要的注意事项:

  • 键的唯一性: HashMap中的键必须是唯一的。如果尝试将相同的键插入HashMap中,新值将覆盖旧值。
  • 值可以重复: HashMap中的值可以重复。多个键可以映射到相同的值。
  • 空键: HashMap允许使用null作为键,但只能有一个null键。这意味着如果插入多个null键,后续的null键将覆盖前面的。
  • 并发性: HashMap不是线程安全的,如果在多个线程之间共享HashMap,请确保适当地同步访问,或者使用ConcurrentHashMap等线程安全的集合。
  • 性能: HashMap的性能通常很好,但在某些情况下,可能会发生哈希冲突,导致性能下降。要优化性能,可以考虑调整HashMap的初始容量和负载因子。
  • 哈希函数: HashMap使用哈希函数将键映射到存储位置。如果键的哈希码分布不均匀,可能会导致哈希冲突。因此,确保自定义对象的hashCode方法正确实现,以获得更好的性能。
  • 遍历顺序: HashMap的遍历顺序不是按照插入顺序或任何特定顺序的。如果需要按特定顺序访问键值对,可以考虑使用LinkedHashMap。
  • 容量和负载因子: 考虑根据数据量选择适当的初始容量和负载因子。较大的初始容量可以减少哈希冲突,提高性能。
  • 空间复杂度: HashMap的空间复杂度是O(n),其中n是存储的键值对数量。因此,要谨慎使用大型HashMap,以避免内存占用过多。
  • 使用泛型: 在创建HashMap时,尽可能使用泛型来指定键和值的类型,以提高类型安全性。
  • 及时清理不再需要的键值对: 如果不再需要HashMap中的某个键值对,及时使用remove方法或其他方式删除它们,以释放内存和资源。
  • 异常处理: 当使用get方法获取值时,要考虑键不存在的情况,以避免NullPointerException。可以使用containsKey方法或条件语句来检查键是否存在。
  • 性能监控: 如果HashMap用于性能关键的应用程序,考虑使用性能监控工具来分析和优化HashMap的使用。
  • 备份: 定期备份HashMap中的重要数据,以防止数据丢失或损坏。

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言