Java HashSet和Java HashMap

在 Java 中,HashSet和HashMap是非常重要的数据结构,它们广泛应用于各种场景中。

HashSet

HashSet 的基本概念

HashSet是Set接口的一个实现,其底层实际上使用了HashMap来存储元素。这使得HashSet能够提供 O(1) 的平均时间复杂度来执行基本操作,如添加、删除和检查元素是否存在。

HashSet 允许有 null 值。

HashSet 是无序的,即不会记录插入的顺序。

HashSet 不是线程安全的, 如果多个线程尝试同时修改 HashSet,则最终结果是不确定的。 您必须在多线程访问时显式同步对 HashSet 的并发访问。

HashSet是一个不包含重复元素的集合。

特点

  • 不重复HashSet 会自动去除重复的元素。
  • 无序:元素的顺序是不确定的,因为它是基于哈希表实现的。
  • 可变:可以在运行时动态添加或删除元素。
  • 非线程安全 :如果多个线程同时修改 HashSet,则需要外部同步。

HashSet 的内部实现

HashSet 的内部实际上是通过 HashMap 来实现的。它使用 HashMap 的 key 来存储元素,而 value 则是一个静态的私有对象 PRESENT。这是因为 HashSet 只关心 key 的值而不关心 value 的值。

哈希码

为了保证元素的唯一性,HashSet 会使用元素的哈希码(hashCode() 方法)来确定元素的位置。如果两个元素的哈希码相同,HashSet 会调用 equals() 方法来判断它们是否相等。

冲突解决

当多个元素的哈希码相同(即哈希冲突)时,HashSet 会在相同的桶中使用链表或红黑树来存储这些元素。在 Java 8 中,当链表长度达到 8 时,链表会转换为红黑树以提高查找效率。

注意事项

  • 自定义对象 :当你向 HashSet 中添加自定义的对象时,需要重写 hashCode()equals() 方法,以确保正确地识别重复元素。
  • 空元素HashSet 允许添加一个 null 元素。
  • 性能考虑 :虽然 HashSet 的性能通常很好,但在极端情况下,例如大量哈希冲突,性能可能会下降。

示例代码

java 复制代码
import java.util.HashSet;
import java.util.Set;

public class HashSetExample {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        
        // 添加元素
        set.add("Java");
        set.add("C++");
        set.add("Python");
        set.add("Java"); // 这个元素不会被添加,因为已经存在
        
        // 输出集合
        System.out.println(set);
        
        // 检查元素是否存在
        boolean exists = set.contains("Java");
        System.out.println("Java exists in the set: " + exists);
        
        // 删除元素
        set.remove("C++");
        System.out.println(set);
    }
}

HashMap

HashMap是一种基于哈希表实现的Map接口的实现。它提供了快速的插入、删除和查找操作HashMap允许存储键值对,其中键不能重复,而值可以重复。

HashMap 的基本概念

HashMap是Map接口的一个实现,它允许存储键值对,其中键是唯一的。这意味着每个键只能对应一个值,如果尝试使用相同的键存储不同的值,则旧的值会被新的值替换。

特点

  • 键唯一:每个键只能对应一个值。
  • 值可重复:不同的键可以对应相同的值。
  • 非线程安全 :如果多个线程同时修改 HashMap,则需要外部同步。
  • 可变:可以在运行时动态添加或删除键值对。
  • 无序:元素的顺序是不确定的,因为它是基于哈希表实现的。

HashMap 的内部实现

HashMap的内部是一个数组加链表(或红黑树)的结构。数组中的每个位置称为一个桶(Bucket),每个桶中可能包含一个链表或红黑树,用来处理哈希冲突。

哈希码

为了定位键值对,HashMap使用键的哈希码(hashCode()方法)来确定元素在数组中的位置。如果两个键的哈希码相同(即哈希冲突),HashMap会在相同的桶中使用链表或红黑树来存储这些键值对。

冲突解决

在 Java 8 中,当链表长度达到 8 时,链表会转换为红黑树以提高查找效率。当链表长度再次减少到某个阈值以下时,红黑树会转换回链表。

扩容机制

当HashMap的容量不足以容纳更多的元素时,它会自动进行扩容。扩容过程中,会创建一个新的更大的数组,并将原有数组中的元素重新哈希并复制到新数组中。默认的初始容量为 16,负载因子为 0.75。

注意事项

  • 自定义对象 :当你向 HashMap 中添加自定义的对象作为键时,需要重写 hashCode()equals() 方法,以确保正确地识别键。
  • 空键与空值HashMap 允许一个 null 键和任意数量的 null 值。
  • 性能考虑 :虽然 HashMap 的性能通常很好,但在极端情况下,例如大量哈希冲突,性能可能会下降。

示例代码

java 复制代码
import java.util.HashMap;
import java.util.Map;

public class HashMapExample {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        
        // 添加键值对
        map.put("One", 1);
        map.put("Two", 2);
        map.put("Three", 3);
        
        // 输出所有键值对
        for (Map.Entry<String, Integer> entry : map.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }
        
        // 获取值
        int value = map.get("Two");
        System.out.println("Value of Two: " + value);
        
        // 更新值
        map.put("Two", 22);
        System.out.println(map);
        
        // 删除键值对
        map.remove("Two");
        System.out.println(map);
    }
}

总结

  • HashSetHashMap 都是基于哈希表的数据结构。
  • HashSet 用于存储不重复的元素集合,底层使用 HashMap 实现。
  • HashMap 用于存储键值对,其中键是唯一的。
  • 两者都提供了快速的操作,时间复杂度通常为 O(1)。
相关推荐
知我心·1 分钟前
Java 正则表达式知识点总结
java
indexsunny1 分钟前
互联网大厂Java面试实战:微服务与Spring生态技术解析
java·spring boot·redis·kafka·mybatis·hibernate·microservices
狂奔蜗牛飙车1 分钟前
Python学习之路-Python3 迭代器与生成器学习详解
开发语言·python·学习·#python学习笔记·python迭代器生成器
小疙瘩5 分钟前
去掉 IDEA 中 mybatis配置文件的局部背景颜色(图解)
java·ide·intellij-idea
xqqxqxxq5 分钟前
洛谷算法1-3 暴力枚举(NOIP经典真题解析)java(持续更新)
java·开发语言·算法
爱上妖精的尾巴6 分钟前
8-5 WPS JS宏 match、search、replace、split支持正则表达式的字符串函数
开发语言·前端·javascript·wps·jsa
阿猿收手吧!6 分钟前
【C++】inline变量:全局共享新利器
开发语言·c++
沐知全栈开发7 分钟前
Python3 列表详解
开发语言
逝水如流年轻往返染尘8 分钟前
正则表达式字符串
java·正则表达式
LYS_06188 分钟前
寒假学习(14)(HAL库5)
java·linux·学习