Java:HashMap的使用

目录

一、概念

二、常用操作

[2.1 初始化](#2.1 初始化)

[2.2 CRUD](#2.2 CRUD)

[2.3 遍历](#2.3 遍历)

[2.4 其他常用方法](#2.4 其他常用方法)

三、与其他Map的比较


一、概念

HashMap 位于 java.util 包下。它实现了 Map 接口,提供了基于键值对(Key-Value)的数据存储方式,并允许使用 null 作为键和值。

核心思想:使用"键(Key)"的哈希值来存储和检索"值(Value)"。

核心特点:

  • 基于哈希表 :它使用哈希算法来存储和检索数据,这使得在理想情况下,getput 操作的时间复杂度可以达到 O(1)

  • 无序HashMap 不保证其中元素的顺序(即插入顺序和访问顺序),并且顺序也可能会随时间(如扩容时)而变化。如果需要有序,可以使用 LinkedHashMap

  • 非线程安全 :多个线程同时操作一个 HashMap 可能会导致数据不一致。如果多个线程同时访问一个 HashMap 并至少有一个线程修改了它,则必须在外部进行同步。或者使用 Collections.synchronizedMap() 进行包装,或者更推荐使用 ConcurrentHashMap

二、常用操作

2.1 初始化

java 复制代码
// 1. 最常见的无参构造,默认初始容量16,负载因子0.75
HashMap<String, Integer> map = new HashMap<>();

// 2. 指定初始容量(减少扩容次数,优化性能)
HashMap<String, Integer> mapWithCapacity = new HashMap<>(32);

// 3. 指定初始容量和负载因子(高级用法,通常不需要)
HashMap<String, Integer> mapWithFactor = new HashMap<>(32, 0.8f);

// 4. 通过另一个Map来创建
HashMap<String, Integer> anotherMap = new HashMap<>(map);

扩容机制:扩充为原数组容量的2倍

当 HashMap 中的元素数量size超过当前阈值(threshold) 时,就会触发扩容。

阈值 (threshold) = 容量 (capacity) * 负载因子 (load factor)

  • 默认示例:默认容量为 16,默认负载因子为 0.75。

    • 那么阈值就是 16 * 0.75 = 12

    • 当执行 put() 操作后,size 变得大于 12(即 13)时,就会触发扩容。

2.2 CRUD

java 复制代码
HashMap<String, String> capitalCities = new HashMap<>();

// 添加与更新
// put(K key, V value) - 添加键值对,如果key已存在,则更新其value
capitalCities.put("USA", "Washington D.C.");
capitalCities.put("Germany", "Berlin");
capitalCities.put("Germany", "Berlin"); // 重复放入,不会改变
capitalCities.put("Germany", "New Berlin"); // Key已存在,Value会被更新为 "New Berlin"

// putIfAbsent(K key, V value) - (Java 8+) 只有在key不存在或对应的value为null时,才放入
capitalCities.putIfAbsent("France", "Paris"); // 会放入,因为France不存在
capitalCities.putIfAbsent("Germany", "Paris"); // 不会放入,因为Germany已存在,Value仍然是"New Berlin"


// 获取元素
// get(Object key) - 根据key获取value,如果key不存在,返回null
String capitalOfGermany = capitalCities.get("Germany"); // "New Berlin"
String capitalOfJapan = capitalCities.get("Japan"); // null

// getOrDefault(Object key, V defaultValue) - (Java 8+) key不存在时返回一个默认值
String capitalOfJapanSafe = capitalCities.getOrDefault("Japan", "Not Found"); // "Not Found"


// 检查元素是否存在
// containsKey(Object key) - 检查某个key是否存在
boolean hasGermany = capitalCities.containsKey("Germany"); // true
boolean hasJapan = capitalCities.containsKey("Japan"); // false

// containsValue(Object value) - 检查某个value是否存在(效率较低,需要遍历)
boolean hasParis = capitalCities.containsValue("Paris"); // true


// 删除元素
// remove(Object key) - 根据key删除键值对,返回被删除的value
String removedValue = capitalCities.remove("Germany"); // removedValue = "New Berlin"

// remove(Object key, Object value) - (Java 8+) 只有当key和value都匹配时才删除
boolean isRemoved = capitalCities.remove("USA", "LA"); // false, 因为Value不匹配"Washington D.C.",删除失败
boolean isRemoved2 = capitalCities.remove("USA", "Washington D.C."); // true, 删除成功

2.3 遍历

java 复制代码
// 遍历所有键:keySet()
for (String country : capitalCities.keySet()) {
    System.out.println("Country: " + country);
    // 可以通过key再get value,但效率较低(不推荐在循环内这样用)
    // System.out.println("Capital: " + capitalCities.get(country));
}

// 遍历所有值:values()
for (String capital : capitalCities.values()) {
    System.out.println("Capital: " + capital);
}

// 遍历所有键值对:entrySet() (最推荐、最高效的方式)
for (Map.Entry<String, String> entry : capitalCities.entrySet()) {
    String country = entry.getKey();
    String capital = entry.getValue();
    System.out.println(country + " -> " + capital);
}

// 使用 Java 8 forEach + Lambda 表达式 (最简洁)
capitalCities.forEach((country, capital) -> {
    System.out.println(country + " -> " + capital);
});

2.4 其他常用方法

java 复制代码
// size() - 返回键值对的数量
int size = capitalCities.size();

// isEmpty() - 判断是否为空
boolean isEmpty = capitalCities.isEmpty();

// clear() - 清空所有映射
capitalCities.clear();

// replace(K key, V oldValue, V newValue) - (Java 8+) 替换操作
capitalCities.replace("France", "Paris", "Lyon"); // 只有当旧值匹配时才替换

三、与其他Map的比较

特性 HashMap LinkedHashMap TreeMap Hashtable ConcurrentHashMap
排序保证 无顺序 插入顺序访问顺序 (LRU) 键的自然顺序自定义比较器顺序 无顺序 无顺序
是否允许 null 允许 一个 null key 和多个 null value 允许 一个 null key 和多个 null value 不允许 null key (取决于Comparator) 不允许 null key 或 null value 不允许 null key 或 null value
线程安全 (非同步) (非同步) (非同步) (同步,每个方法都用 synchronized 修饰) (采用分段锁/CAS等更高效的并发控制)
性能特点 O(1) 时间复杂度的 get/put (平均情况) 比 HashMap 稍慢,因为要维护链表 O(log n) 时间复杂度的 get/put 类似 HashMap,但同步开销大,性能差 高并发性能极佳,读操作通常无需锁
底层实现 数组 + 链表/红黑树 (哈希表) HashMap + 双向链表 (维护顺序) 红黑树 数组 + 链表 (哈希表) 数组 + 链表/红黑树 + CAS + 分段锁 (JDK 7/8 不同)
迭代顺序一致性 不保证,甚至可能随时间变化 保证,与插入顺序或访问顺序一致 保证,根据键的顺序排序 不保证 不保证
引入版本 JDK 1.2 JDK 1.4 JDK 1.2 JDK 1.0 (是古老遗留类) JDK 1.5 (java.util.concurrent 包)
相关推荐
hqxstudying4 分钟前
JAVA限流方法
java·开发语言·安全·限流
shylyly_11 分钟前
Linux->多线程2
java·linux·多线程·线程安全·线程同步·线程互斥·可重入
小蒜学长1 小时前
基于实例教学的软件工程专业教学系统
java·spring boot·后端·软件工程
小楓12011 小时前
MySQL數據庫開發教學(二) 核心概念、重要指令
开发语言·数据库·mysql
Code_Artist1 小时前
[Java并发编程]3.同步锁的原理
java·后端·面试
渣哥1 小时前
面试必问!JDK动态代理和CGLIB动态代理的核心区别
java
天天摸鱼的java工程师1 小时前
如何实现数据实时同步到 ES?八年 Java 开发的实战方案(从业务到代码)
java·后端·面试
掉鱼的猫1 小时前
老码农教你:Solon + EasyExcel 导出工具
java·excel
一枚小小程序员哈2 小时前
基于php的萌宠社区网站的设计与实现、基于php的宠物社区论坛的设计与实现
开发语言·php·宠物