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)。
相关推荐
bingo6919 分钟前
Qt-常用控件(3)-多元素控件、容器类控件和布局管理器
开发语言·qt
蒙娜丽宁9 分钟前
深入探讨Go语言中的切片与数组操作
开发语言·后端·golang·go
GISer小浪花努力上岸1 小时前
Java实现简易计算器功能(idea)
java·开发语言·intellij-idea
2401_858120531 小时前
深入理解 Swift 中的隐式解包可选类型(Implicitly Unwrapped Optionals)
开发语言·ios·swift
quaer1 小时前
QT chart案例
开发语言·qt·swift
海海向前冲2 小时前
设计模式 -- 单例设计模式
java·开发语言·设计模式
就这样很好8802 小时前
排序算法总结
java·算法·排序算法
TT-Kun2 小时前
C++ | vector 详解
开发语言·c++
weixin_486681142 小时前
C++系列-STL中find相关的算法
java·c++·算法
陈大爷(有低保)2 小时前
JS基础语法
开发语言·javascript·ecmascript