HashMap集合底层实现原理与注解

本文深入探讨HashMap的底层实现,包括其结合数组与链表的结构,散列函数的工作原理,以及如何通过hashCode处理数据冲突。同时,介绍了HashSet的实现方式,并解析了自定义注解的创建与使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

HashMap集合底层实现原理

数组的特点是:寻址容易,插入和删除困难;
链表的特点是:寻址困难,插入和删除容易。
哈希表:->数组+链表组成–>线性数组

HashSet:由哈希表(实际上是一个HashMap实例)支持。无序。不允许有重复元素。HashSet中的元素都存放在HashMap的key上面,而value中的值都是统一的一个private static final Object PRESENT = new Object();。HashSet跟HashMap一样,都是一个存放链表的数组。
注意:HashSet保存的对象如果重写了equals 和 hasCode方法,会有意想不到的结果。

散列函数:
在java中,每个对象都会拥有一个hashCode()方法,
这就是散列函数,通过这个方法会返回一个32位的整数。
使用这么大的值作为散列值是为了尽量避免碰撞,
例如两个不同对象的hashCode一样的话就是碰撞了。

HashMap:里面实现一个静态内部类Entry,其重要的属性有 key , value, next
HashMap的基础就是一个线性数组,这个数组就是Entry[],Map里面的内容都保存在Entry[]里面。

向HashMap中添加数据 : map.put(“key”,“value”);
实质:int hashCode = key.hashCode();//key相同hashCode一定相同(没有重写hashCode的情况下)
int index = hashCode % Entry[].length;
Entry[index] = value;

从HashMap中取数据 : map.get(“key”);
实质:int hashCode = key.hashCode();
int index = hashCode % Entry[].length;
return Entry[index];

注意:
如果key相同的情况下,会覆盖上可以key所对应的value值。
Entry中的next属性就起作用了。
Entry 中 next : 指向下一个Entry。

eg:
map.put(“name”,“tom”);

//假设这次key-value 为 Entry a = “name”:“tom”;
//int index = “name”.hashCode() % Entry[].length;//假设这里的index是0
//Entry[0] = a;

map.put(“name”,“李四”);

//假设这次key-value 为 Entry b = “name”:“李四”;
//int index = “name”.hashCode() % Entry[].length;//这里的index是必然也是0
//不同的操作;b.next = a;
//Entry[0] = b;

map.put(“name”,“王五”);

//假设这次key-value 为 Entry c = “name”:“王五”;
//int index = “name”.hashCode() % Entry[].length;//这里的index是必然也是0
//不同的操作;c.next = b;
//Entry[0] = c;

如果key不相同:新加入的数据放在链头,原先加入的数据放到链尾,—> Entry[] = [ Entry_c(“name”,“王五”,“next->Entry_b”) ];

Entry_b(“name”,“李四”,“next->Entry_a”)

Entry_a(“name”,“tom”,“next”)

注解

十二.五:注解:
@Target

表示注解能写在什么上面。作用于哪里。
METHOD			可用于方法上 
TYPE			可用于类或者接口上 
FIELD			可用于属性上 
PARAMETER		可用于参数上 
CONSTRUCTOR		可用于构造方法上 

ANNOTATION_TYPE		可用于注解类型上(被 @interface修饰的类型) 
LOCAL_VARIABLE		可用于局部变量上 
PACKAGE			用于记录java文件的package信息 

@Retention 保留
定义了该Annotation被保留的时间长短
1,仅出现在源代码中,而被编译器丢弃
2,被编译在class文件中

1.SOURCE:在源文件中有效(即源文件保留)
    2.CLASS:在class文件中有效(即class保留)
    3.RUNTIME:在运行时有效(即运行时保留)
@Documented 文档API
annotation应该被作为被标注的程序成员的公共API
@Inherited 被继承的
@Inherited阐述了某个被标注的类型是被继承的
@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类

自定义注解:
public @interface A {}
给注解定义属性:
权限 参数 属性名 默认 值
public String value() default “”;

注:权限只有public 和 默认的
参数支持的数据类型:
基本数据类型
String类型
Class类型,
可以添加泛型,以及上面的数组类型。

<think>好的,我现在需要帮助用户了解如何在Java中使用HashMap实现键值对映射。根据用户的问题,他们可能对HashMap的基本用法、实现原理以及相关注意事项感兴趣。首先,我应该整理用户提供的引用内容,看看有哪些信息可以直接利用。 用户提供了四个引用,其中引用[1]提到了HashMap的基本操作,包括创建实例、添加、获取、检查、删除和遍历键值对。引用[2]和[4]讨论了如何排序HashMap,而引用[3]强调了键对象必须正确实现hashCode()和equals()方法。这些信息都是回答用户问题的重要部分。 首先,我需要从基础开始,逐步引导用户了解HashMap的使用。应该先介绍如何创建HashMap实例,然后依次讲解添加、获取、检查存在性、删除和遍历操作。这部分内容在引用[1]中有示例代码,可以直接参考。例如,创建实例的部分需要导入java.util.HashMap,并初始化泛型类型。 接下来,键对象的hashCode()和equals()方法的正确实现是关键,这一点在引用[3]中提到。需要解释为什么这两个方法很重要,以及如何正确覆盖它们,尤其是在使用自定义对象作为键时。比如,如果两个对象相等,它们的hashCode必须相同,否则会导致HashMap无法正确找到对应的值。 关于排序,用户可能想知道虽然HashMap本身无序,但可以通过转换为List并排序来实现,如引用[2]和[4]所述。这部分可以作为进阶内容,说明如何按键或值排序,可能需要使用Comparator和Collections.sort方法。 在实现方式方面,用户可能对HashMap底层数据结构感兴趣。需要解释HashMap是基于哈希表实现的,使用数组和链表(或红黑树,在Java 8之后)来处理冲突。这涉及负载因子、扩容机制等概念,这些内容在引用[2]的源码分析部分有所提及。 还要注意示例代码的正确性,确保语法无误,比如泛型的使用和方法的正确调用。例如,在遍历键值对时,使用entrySet()获取所有条目,然后遍历每个Entry对象来获取键和值。 最后,生成相关问题时要涵盖用户可能进一步探索的方向,比如性能优化、线程安全、处理冲突的方法以及其他集合类的比较。这些问题可以帮助用户深入理解HashMap的应用场景和限制。 总结一下,回答的结构应该包括基本用法、实现原理、注意事项和进阶操作,每个部分结合引用内容,确保信息准确。同时,示例代码要清晰,注释详细,帮助用户快速上手。</think>### 使用HashMap实现键值对映射 #### 1. **HashMap的基本用法** HashMapJava中基于哈希表的键值对存储结构,支持高效插入、删除和查找操作。以下是核心实现步骤: **1.1 创建HashMap实例** ```java import java.util.HashMap; public class Main { public static void main(String[] args) { // 声明键为String类型,值为Integer类型的HashMap HashMap<String, Integer> map = new HashMap<>(); } } ``` 键值类型需通过泛型指定,支持任意对象类型[^1]。 **1.2 添加键值对** ```java map.put("apple", 10); // 添加键"apple"对应值10 map.put("banana", 5); // 添加键"banana"对应值5 ``` 若键已存在,新值会覆盖旧值[^1]。 **1.3 获取值** ```java int count = map.get("apple"); // 返回10 int missing = map.getOrDefault("grape", 0); // 键不存在时返回默认值0 ``` **1.4 检查键/值存在性** ```java boolean hasKey = map.containsKey("apple"); // true boolean hasValue = map.containsValue(5); // true ``` **1.5 删除键值对** ```java map.remove("banana"); // 删除键为"banana"的条目 ``` **1.6 遍历操作** ```java // 遍历所有键值对 for (HashMap.Entry<String, Integer> entry : map.entrySet()) { System.out.println(entry.getKey() + ": " + entry.getValue()); } ``` --- #### 2. **实现原理** - **数据结构**:基于数组+链表(Java 8后链表长度>8时转为红黑树)[^2]。 - **哈希函数**:键的`hashCode()`计算哈希值,决定存储位置。 - **冲突解决**:链表法处理哈希冲突,Java 8优化为红黑树提升查询效率[^2]。 - **扩容机制**:默认负载因子$0.75$,容量翻倍时重新哈希[^2]。 --- #### 3. **键对象的特殊要求** - **`hashCode()``equals()`**:必须正确覆盖这两个方法,保证: - 相等的对象必须返回相同的哈希值。 - 不相等的对象尽量返回不同的哈希值(减少冲突)[^3]。 **示例:自定义键对象** ```java class Student { String id; @Override public int hashCode() { return id.hashCode(); } @Override public boolean equals(Object obj) { return obj instanceof Student && this.id.equals(((Student)obj).id); } } ``` --- #### 4. **进阶操作:排序** HashMap本身无序,但可通过以下方式排序: ```java // 按键排序 List<String> sortedKeys = new ArrayList<>(map.keySet()); Collections.sort(sortedKeys); // 按值排序(需借助Stream API) List<Map.Entry<String, Integer>> sortedEntries = map.entrySet() .stream() .sorted(Map.Entry.comparingByValue()) .collect(Collectors.toList()); ``` 此方法利用了列表和比较器实现排序[^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值