HashSet

目录

一.HashSet的实现原理

1.添加元素

2.删除元素

3.判断元素是否存在

4.迭代器

5.性能

6.并发性

7.序列化

二.HashSet的线程安全性

三.HashSet的有序性


一.HashSet的实现原理

        HashSet 是基于 HashMap 实现的,HashSet的值存放于HashMap的key上,HashMap的value统一为present,因此 HashSet 的实现比较简单,相关 HashSet 的操作,基本上都是直接调用底层HashMap 的相关方法来完成,HashSet 不允许重复的值。

1.添加元素

        HashSet 的add()方法用于向集合中添加元素。这个方法内部调用HashMap的put()方法,将元素作为键插入到 HashMap 中。

public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

2.删除元素

  HashSet 的remove()方法用于从集合中删除指定的元素。这个方法内部调用 HashMap的 remove()方法,将对应的键(即元素)从 HashMap中移除。

    public boolean remove(Object o) {
        return map.remove(o)==PRESENT;
    }

3.判断元素是否存在

  HashSet 的contains()方法用于判断集合中是否包含指定的元素。这个方法内部调用 HashMap的containsKey()方法,判断 HashMap中是否存在该键(即元素)。

public boolean contains(Object o) {
    return map.containsKey(o);
}

4.迭代器

  返回对此 set 中元素进行迭代的迭代器。返回元素的顺序并不是特定的。

public Iterator<E> iterator() {
    return map.keySet().iterator();
}

5.性能

  HashSet 的添加、删除和查找操作的性能通常都是常数时间的(O(1)),这得益于 HashMap的性能。

6.并发性

        HashSet不是线程安全的。如果你需要线程安全的集合,可以使用Collections.synchronizedSet(new HashSet<>(...))或者ConcurrentHashMap的键集。

7.序列化

        HashSet 实现了 Serializable 接口,这意味着它可以被序列化和反序列化。

二.HashSet的线程安全性

        HashSet 是非线程安全的。这意味着多个线程同时修改 HashSet可能会导致不可预知的行为。如果多个线程需要访问和修改同一个 HashSet实例,必须通过外部同步来确保线程安全。

如果你需要线程安全的Set集合,可以考虑以下替代方案:

(1)Collections.synchronizedSet:通过这个静态方法包装一个 HashSet,可以提供简单的线程安全。但是,这种方法的并发性能较低,因为每次访问都需要获取锁。

(2)ConcurrentHashMap 的 keySet 方法:ConcurrentHashMap 提供了 keySet 方法,它返回一个线程安全的 Set 视图。这个 Set 支持更高效的并发访问。

(3)CopyOnWriteArraySet:这是一个线程安全的 Set 实现,它在每次修改(添加或删除元素)时都会复制整个底层数组。这种方法适用于读多写少的场景。

        // 使用 Collections.synchronizedSet 包装 HashSet
        Set<String> syncSet = Collections.synchronizedSet(new HashSet<>());
        syncSet.add("Element1");
        syncSet.add("Element2");

        // 使用 CopyOnWriteArraySet
        Set<String> copyOnWriteSet = new CopyOnWriteArraySet<>();
        copyOnWriteSet.add("Element1");
        copyOnWriteSet.add("Element2");

        // 迭代两个线程安全的 Set
        System.out.println("Synchronized Set: " + syncSet);
        System.out.println("CopyOnWriteArraySet: " + copyOnWriteSet);

三.HashSet的有序性

        HashSet 不保证元素的顺序。它基于 HashMap实现,而 HashMap不保证键的顺序。因此,每次迭代 HashSet 时元素的顺序可能不同。

如果你需要一个有序的Set集合,你可以考虑以下几种方法:

(1)TreeSet: TreeSet 是一个有序的 Set集合,它基于红黑树实现。TreeSet可以确保元素处于排序状态,无论是按照自然顺序还是根据提供的 Comparator。

(2)LinkedHashSet: LinkedHashSet维护了元素的插入顺序,或者在构造时指定的最近期访问顺序。它内部使用 LinkedList 来维护元素的顺序,同时使用 HashMap 来保证元素的唯一性。

(3)使用 HashSet 与额外的数据结构: 如果你需要保留 HashSet 的所有特性,并且只需要偶尔的顺序访问,你可以在需要的时候将 HashSet 元素添加到一个 TreeSet或 LinkedHashSet 中,进行排序操作。

        Set<Integer> hashSet = new HashSet<>();
        hashSet.add(4);
        hashSet.add(1);
        hashSet.add(3);
        hashSet.add(2);

        // 仅在需要有序时进行排序
        Set<Integer> sortedSet = new TreeSet<>(hashSet);
        for (Integer number : sortedSet) {
            System.out.println(number);
        }
        LinkedHashSet<Integer> linkedHashSet = new LinkedHashSet<>(hashSet);
        for (Integer number : linkedHashSet) {
            System.out.println(number);
        }

### HashSet 介绍 HashSetJava 集合框架中的一个类,它实现了 Set 接口,基于哈希表(实际上是一个 HashMap 实例)来存储元素。HashSet 不允许存储重复的元素,并且不保证元素的顺序,因为它不维护元素的插入顺序或其他特定顺序。哈希技术是 Java 集合框架的基石,深入理解 HashSet 的实现原理,能提升代码性能,培养底层思维[^1]。 ### 使用方法 #### 1. 创建 HashSet ```java import java.util.HashSet; public class HashSetExample { public static void main(String[] args) { // 创建一个 HashSet 对象 HashSet<String> set = new HashSet<>(); // 使用 with_hasher 方法创建具有指定哈希函数的 HashSet(这里只是示例语法,实际 Java 里不是这种写法) // 假设存在自定义哈希函数 MyHasher // HashSet<String, MyHasher> setWithHasher = HashSet.with_hasher(new MyHasher()); } } ``` #### 2. 添加元素 ```java import java.util.HashSet; public class HashSetExample { public static void main(String[] args) { HashSet<String> set = new HashSet<>(); // 添加元素 boolean result1 = set.add("apple"); boolean result2 = set.add("banana"); boolean result3 = set.add("apple"); // 重复元素,添加失败 System.out.println(result1); // 输出: true System.out.println(result2); // 输出: true System.out.println(result3); // 输出: false } } ``` 这里的 `add` 方法有返回值,如果传入的元素是集合中不存在的新值,返回 `true` 表示添加成功;如果传入的是集合中已存在的重复值,返回 `false` 表示添加失败,其内部实现是适配到 `HashMap` 上完成的,`HashSet` 的 `add` 方法通过 `HashMap.put` 方法的返回结果来判断是否添加成功[^3][^5]。 #### 3. 判断元素是否存在 ```java import java.util.HashSet; public class HashSetExample { public static void main(String[] args) { HashSet<String> set = new HashSet<>(); set.add("apple"); boolean contains = set.contains("apple"); System.out.println(contains); // 输出: true } } ``` #### 4. 删除元素 ```java import java.util.HashSet; public class HashSetExample { public static void main(String[] args) { HashSet<String> set = new HashSet<>(); set.add("apple"); boolean removed = set.remove("apple"); System.out.println(removed); // 输出: true } } ``` #### 5. 遍历元素 ```java import java.util.HashSet; import java.util.Iterator; public class HashSetExample { public static void main(String[] args) { HashSet<String> set = new HashSet<>(); set.add("apple"); set.add("banana"); // 使用迭代器遍历 Iterator<String> iterator = set.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); } // 使用增强 for 循环遍历 for (String element : set) { System.out.println(element); } } } ``` ### 相关技术细节 - **哈希函数**:`HashSet` 依赖于元素的 `hashCode` 方法来确定元素在哈希表中的存储位置。`Object` 类的 `hashCode` 方法返回的哈希码具有地址唯一性,但为了让程序的运行逻辑符合现实生活(即属性相同的对象被看作同一个对象),`Object` 的子类通常会重写 `hashCode` 方法,基本数据类型的实现类都已经重写了 `hashCode` 和 `equals` 方法,自定义的类需要开发者自己重写这两个方法[^4]。 - **内部实现**:`HashSet` 内部使用 `HashMap` 来存储元素,`HashSet` 的元素作为 `HashMap` 的键,而 `HashMap` 的值则是一个固定的对象。`HashSet` 的操作实际上是适配到 `HashMap` 上完成的,例如 `HashSet` 的 `add` 方法通过 `HashMap` 的 `put` 方法来实现[^3][^5]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值