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)。
相关推荐
01漫游者2 分钟前
JavaScript函数与对象增强知识
开发语言·javascript·ecmascript
GottdesKrieges3 分钟前
OceanBase恢复常见问题
java·数据库·oceanbase
IGAn CTOU4 分钟前
Java高级开发进阶教程之系列
java·开发语言
leo825...7 分钟前
Claude Code Skills 清单(本地)
java·python·ai编程
csbysj202010 分钟前
SQL NULL 函数详解
开发语言
其实防守也摸鱼13 分钟前
CTF密码学综合教学指南--第三章
开发语言·网络·python·安全·网络安全·密码学
NGSI vimp13 分钟前
Java进阶——如何查看Java字节码
java·开发语言
We་ct1 小时前
深度剖析浏览器跨域问题
开发语言·前端·浏览器·跨域·cors·同源·浏览器跨域
身如柳絮随风扬1 小时前
多数据源切换实战:从业务场景到3种实现方案全解析
java·分布式·微服务
skywalk81631 小时前
在考虑双轨制,即在中文语法的基础上,加上数学公式的支持,这样像很多计算将更加简单方便,就像现在的小学数学课本里面一样,比如:定x=2*x + 1
开发语言