Java HashMap详解

HashMap是Java中一个经典的数据结构,它实现了Map接口,提供了一种键值对的映射关系。因为它的高效性和易用性,HashMap在Java编程中被广泛使用。

一、HashMap的基本概念

HashMap是一个散列表,它存储的是键值对(key-value)映射的数据。

在HashMap中,每个键(key)都是唯一的,对应着一个值(value)。你可以通过键来访问对应的值,类似于字典。在Java中,键和值都可以是任何对象。

二、HashMap的构造方法

HashMap类有多个构造方法,其中比较常用的是以下两个:

/**
 * 创建一个空的HashMap
 */
HashMap()

/**
 * 创建一个具有指定初始容量和默认负载因子(0.75)的HashMap
 *
 * @param initialCapacity 初始容量
 */
HashMap(int initialCapacity)

第一个构造方法用来创建一个空的HashMap。第二个构造方法用来创建一个具有指定初始容量(initialCapacity)的HashMap。初始容量指的是HashMap能够容纳键值对的数量,当HashMap中存储的键值对数量超过了这个值,HashMap会自动进行扩容。

三、HashMap的常用方法

HashMap提供了许多常用的方法,下面介绍一些比较重要的方法:

1、put()

put()方法用来将键值对插入到HashMap中。如果给定的键已经存在了,则它对应的值会被更新为新的值。

/**
 * 将指定的键和值插入到HashMap中,如果键已经存在,则更新对应的值
 * @param key 键
 * @param value 值
 * @return 如果之前存在对应的值,则返回旧的值,否则返回null
 */
V put(K key, V value)

2、get()

get()方法用来根据键来获取对应的值。

/**
 * 根据键来获取对应的值
 * @param key 键
 * @return 值
 */
V get(Object key)

下面是一个简单的示例:

HashMap<String, Integer> map = new HashMap<>();
map.put("apple", 1);
map.put("banana", 2);
map.put("orange", 3);
System.out.println(map.get("apple"));
System.out.println(map.get("banana"));
System.out.println(map.get("orange"));

输出结果:

1
2
3

3、containsKey()

containsKey()方法用来检查HashMap中是否包含指定的键。

/**
 * 检查HashMap中是否包含指定的键
 * @param key 键
 * @return 如果包含指定的键,则返回true,否则返回false
 */
boolean containsKey(Object key)

4、containsValue()

containsValue()方法用来检查HashMap中是否包含指定的值。

/**
 * 检查HashMap中是否包含指定的值
 * @param value 值
 * @return 如果包含指定的值,则返回true,否则返回false
 */
boolean containsValue(Object value)

5、remove()

remove()方法用来根据指定的键来删除对应的键值对。

/**
 * 根据指定的键来删除对应的键值对
 * @param key 键
 * @return 如果存在对应的值,则返回被删除的值,否则返回null
 */
V remove(Object key)

6、keySet()

keySet()方法用来获取HashMap中所有键的集合。

/**
 * 获取HashMap中所有键的集合
 * @return 键的集合
 */
Set<K> keySet()

下面是一个简单的示例:

HashMap<String, Integer> map = new HashMap<>();
map.put("apple", 1);
map.put("banana", 2);
map.put("orange", 3);
Set<String> keys = map.keySet();
for (String key : keys) {
    System.out.println(key + " -> " + map.get(key));
}

输出结果:

banana -> 2
orange -> 3
apple -> 1

四、HashMap的扩容机制

HashMap的扩容机制非常重要。当HashMap中的键值对数量达到一定程度时,HashMap会自动进行扩容,这是为了保证HashMap的高效性。HashMap的扩容机制遵循以下两个原则:

1、容量(capacity)总是2的幂次方

HashMap的容量总是2的幂次方,这是为了方便计算哈希值。当你向HashMap中插入一个键值对时,HashMap会根据键的哈希值来计算该键值对应该放在哪个位置。如果HashMap的容量不是2的幂次方,那么计算哈希值的时候需要进行额外的处理,这会降低HashMap的效率。

2、负载因子(load factor)默认为0.75

负载因子决定了HashMap何时需要进行扩容。负载因子默认为0.75,这意味着当HashMap中存储的键值对数量达到总容量的75%时,HashMap就会进行扩容。这样做的好处是可以保证HashMap的效率,即当HashMap中存储的键值对数量不多时,HashMap的容量也不会过大,从而提高HashMap的检索效率。

下面是一个HashMap扩容的示例:

HashMap<Integer, Integer> map = new HashMap<>();
System.out.println(map.size());
for (int i = 0; i < 30; i++) {
    map.put(i, i);
    System.out.println("put:" + i);
}
System.out.println(map.size());

输出结果:

0
put:0
put:1
put:2
put:3
put:4
put:5
put:6
put:7
put:8
put:9
put:10
put:11
put:12
put:13
put:14
put:15
put:16
put:17
put:18
put:19
put:20
put:21
put:22
put:23
put:24
put:25
put:26
put:27
put:28
put:29
32

从上面的代码可以看出,初始容量为16的HashMap存储了30个键值对后,容量自动扩容为32。

五、HashMap和线程安全性

HashMap并不是线程安全的。如果多个线程同时读写一个HashMap,有可能会出现数据不一致的情况。如果需要在多线程环境中使用HashMap,可以考虑使用ConcurrentHashMap。

下面是一个对HashMap进行读写操作的示例:

HashMap<Integer, Integer> map = new HashMap<>();
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 1000; i++) {
    executorService.submit(() -> {
        for (int j = 0; j < 1000; j++) {
            map.put(j, j);
            System.out.println(map.get(j));
        }
    });
}
executorService.shutdown();

输出结果中可能会存在重复的数字,这是因为多个线程同时进行读写操作导致的。

下面是对ConcurrentHashMap进行读写操作的示例:

ConcurrentHashMap<Integer, Integer> map = new ConcurrentHashMap<>();
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 1000; i++) {
    executorService.submit(() -> {
        for (int j = 0; j < 1000; j++) {
            map.put(j, j);
            System.out.println(map.get(j));
        }
    });
}
executorService.shutdown();

输出结果中不会存在重复的数字,因为ConcurrentHashMap是线程安全的。

六、HashMap的性能分析

HashMap的性能非常优秀,它的常见操作的时间复杂度为O(1)。但是在实际使用中,我们需要注意其扩容机制可能会带来的性能影响。

下面是一个对HashMap进行性能测试的示例:

long start, end;
HashMap<Integer, Integer> map = new HashMap<>();
Random rand = new Random();
start = System.nanoTime();
for (int i = 0; i < 100000; i++) {
    map.put(i, rand.nextInt());
}
end = System.nanoTime();
System.out.println("插入100000个键值对,耗时:" + (end - start) + "ns");

start = System.nanoTime();
for (int i = 0; i < 100000; i++) {
    map.get(rand.nextInt(100000));
}
end = System.nanoTime();
System.out.println("随机访问100000个键值对,耗时:" + (end - start) + "ns");

在我的电脑上,上面的代码的输出结果大概如下:

插入100000个键值对,耗时:10503887ns
随机访问100000个键值对,耗时:68013ns

从上面的代码可以看出,HashMap插入100000个键值对的时间大约为10503887纳秒,随机访问100000个键值对的时间大约为68013纳秒。

七、总结

本文从HashMap的基本概念、构造方法、常用方法、扩容机制、线程安全性和性能分析等多个方面对HashMap进行了详细的介绍。HashMap是Java中一个经典的数据结构,它的高效性和易用性使得它在Java编程中被广泛使用。在使用HashMap时,我们需要注意其扩容机制可能会带来的性能影响,并且需要考虑线程安全性问题。

原创文章,作者:小蓝,如若转载,请注明出处:https://www.506064.com/n/180216.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
小蓝小蓝
上一篇 2024-11-22 05:11
下一篇 2024-11-22 05:11

相关推荐

  • Java JsonPath 效率优化指南

    本篇文章将深入探讨Java JsonPath的效率问题,并提供一些优化方案。 一、JsonPath 简介 JsonPath是一个可用于从JSON数据中获取信息的库。它提供了一种DS…

    编程 2025-04-29
  • java client.getacsresponse 编译报错解决方法

    java client.getacsresponse 编译报错是Java编程过程中常见的错误,常见的原因是代码的语法错误、类库依赖问题和编译环境的配置问题。下面将从多个方面进行分析…

    编程 2025-04-29
  • Java Bean加载过程

    Java Bean加载过程涉及到类加载器、反射机制和Java虚拟机的执行过程。在本文中,将从这三个方面详细阐述Java Bean加载的过程。 一、类加载器 类加载器是Java虚拟机…

    编程 2025-04-29
  • Java腾讯云音视频对接

    本文旨在从多个方面详细阐述Java腾讯云音视频对接,提供完整的代码示例。 一、腾讯云音视频介绍 腾讯云音视频服务(Cloud Tencent Real-Time Communica…

    编程 2025-04-29
  • Java Milvus SearchParam withoutFields用法介绍

    本文将详细介绍Java Milvus SearchParam withoutFields的相关知识和用法。 一、什么是Java Milvus SearchParam without…

    编程 2025-04-29
  • Java 8中某一周的周一

    Java 8是Java语言中的一个版本,于2014年3月18日发布。本文将从多个方面对Java 8中某一周的周一进行详细的阐述。 一、数组处理 Java 8新特性之一是Stream…

    编程 2025-04-29
  • Java判断字符串是否存在多个

    本文将从以下几个方面详细阐述如何使用Java判断一个字符串中是否存在多个指定字符: 一、字符串遍历 字符串是Java编程中非常重要的一种数据类型。要判断字符串中是否存在多个指定字符…

    编程 2025-04-29
  • VSCode为什么无法运行Java

    解答:VSCode无法运行Java是因为默认情况下,VSCode并没有集成Java运行环境,需要手动添加Java运行环境或安装相关插件才能实现Java代码的编写、调试和运行。 一、…

    编程 2025-04-29
  • Java任务下发回滚系统的设计与实现

    本文将介绍一个Java任务下发回滚系统的设计与实现。该系统可以用于执行复杂的任务,包括可回滚的任务,及时恢复任务失败前的状态。系统使用Java语言进行开发,可以支持多种类型的任务。…

    编程 2025-04-29
  • Java 8 Group By 会影响排序吗?

    是的,Java 8中的Group By会对排序产生影响。本文将从多个方面探讨Group By对排序的影响。 一、Group By的概述 Group By是SQL中的一种常见操作,它…

    编程 2025-04-29

发表回复

登录后才能评论