Java集合框架15-HashSet详解

    xiaoxiao2025-04-16  25

    第1部分 HashSet介绍

    HashSet 是一个没有重复元素的集合。 它是由HashMap实现的,不保证元素的顺序,而且HashSet允许使用 null 元素。 HashSet是非同步的。如果多个线程同时访问一个哈希 set,而其中至少一个线程修改了该 set,那么它必须保持外部同步。这通常是通过对自然封装该 set 的对象执行同步操作来完成的。如果不存在这样的对象,则应该使用 Collections.synchronizedSet 方法来“包装” set。最好在创建时完成这一操作,以防止对该 set 进行意外的不同步访问:

    Set s = Collections.synchronizedSet(new HashSet(...));

    HashSet通过iterator()返回的迭代器是fail-fast的。

    此类实现 Set 接口,由哈希表(实际上是一个 HashMap 实例)支持。它不保证set的迭代顺序;特别是它不保证该顺序恒久不变。此类允许使用 null 元素。

    此类为基本操作提供了稳定性能,这些基本操作包括 add、remove、contains 和 size,假定哈希函数将这些元素正确地分布在桶中。对此set进行迭代所需的时间与 HashSet 实例的大小(元素的数量)和底层 HashMap 实例(桶的数量)的“容量”的和成比例。因此,如果迭代性能很重要,则不要将初始容量设置得太高(或将加载因子设置得太低)。

    注意,此实现不是同步的。如果多个线程同时访问一个哈希 set,而其中至少一个线程修改了该 set,那么它必须 保持外部同步。这通常是通过对自然封装该set的对象执行同步操作来完成的。如果不存在这样的对象,则应该使用 Collections.synchronizedSet 方法来“包装” set。最好在创建时完成这一操作,以防止对该set进行意外的不同步访问:  Set s = Collections.synchronizedSet(new HashSet(…));

    此类的 iterator 方法返回的迭代器是fail-fast的:在创建迭代器之后,如果对set进行修改,除非通过迭代器自身的 remove 方法,否则在任何时间以任何方式对其进行修改,Iterator 都将抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败,而不冒将来在某个不确定时间发生任意不确定行为的风险。

    注意,迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。快速失败迭代器在尽最大努力抛出 ConcurrentModificationException。因此,为提高这类迭代器的正确性而编写一个依赖于此异常的程序是错误做法:迭代器的快速失败行为应该仅用于检测 bug。

    此类是 Java Collections Framework 的成员。

    @author Josh Bloch  @author Neal Gafter  @see Collection  @see Set  @see TreeSet  @see HashMap  @since 1.2

    第2部分 HashSet数据结构

    java.lang.Object ↳ java.util.AbstractCollection<E> ↳ java.util.AbstractSet<E> ↳ java.util.HashSet<E> public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable { }

    HashSet与Map关系如下图:

    HashSet<E>:TreeMap存储的是元素。extends AbstractSet<E>:继承了AbstractSet,实现Set接口时需要实现的工作量大大减少了。implements Set<E>:实现了Set,实现了Set中声明的操作set的基本方法。implements Cloneable:表明其可以调用clone()方法来返回实例的field-for-field拷贝。implements Serializable:表明该类是可以序列化的。

    HashSet的构造函数

    /** * 说明HashSet是依赖于HashMap的,底层就是一个HashMap实例。 */ private transient HashMap<E,Object> map; /** * 前面讲了,HashSet是依赖于HashMap的,底层就是一个HashMap实例。HashMap是保存键值对的,但我们保存hashSet的时候肯定只是想保存key,那么调用hashMap(key,value)时value应该传什么值呢?PRESENT就是value。 */ private static final Object PRESENT = new Object();

    构造方法 

    // 默认构造函数 public HashSet() // 带集合的构造函数 public HashSet(Collection<? extends E> c) // 指定HashSet初始容量和加载因子的构造函数 public HashSet(int initialCapacity, float loadFactor) // 指定HashSet初始容量的构造函数 public HashSet(int initialCapacity) // 指定HashSet初始容量和加载因子的构造函数,dummy没有任何作用 HashSet(int initialCapacity, float loadFactor, boolean dummy)

    源码这里就不展示了,从源码中可以看到,HashSet完全依赖于HashMap,几乎没有自己的东西。

    最新回复(0)