美文网首页程序员
HashSet源码分析

HashSet源码分析

作者: 阿斯巴甜不太甜 | 来源:发表于2019-08-15 13:10 被阅读15次

前两天复习数据结构,刚看完散列这一章,于是就想研究一下HashSet和HashMap的源码是怎么实现的。但是大概看了下HashMap,底层貌似用到了红黑树的数据结构,而HashSet比较简单,是基于HashMap实现的,所以这篇文章主要还是分析HashSet。

先来看看他的属性和方法:


image

是不是特别少?事实上除了底下的几个实现的接口以及注释,核心的代码实现可能一共也就几十行。

再来看看他实现的接口和继承的父类:

public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable

HashSet的核心功能都由Set接口和AbstractSet给定义好了。

属性

我们先来看下HashSet的属性:

    static final long serialVersionUID = -5024744406713321676L;

    private transient HashMap<E,Object> map;

    // Dummy value to associate with an Object in the backing Map
    private static final Object PRESENT = new Object();

第一个是序列化ID,第二个就是用来存储元素的一个HashMap。由于使用HashMap的时候只用到了Key,因此定义了一个PRESENT常量来当Value。

构造方法

HashSet一共有五个构造方法,都非常简单。

  1. 无参构造
    public HashSet() {
        map = new HashMap<>();
    }

直接创建一个空的集合

  1. 通过其他容器进行构造
    public HashSet(Collection<? extends E> c) {
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        addAll(c);
    }

只要实现了Collection接口的容器,都可以直接构造成集合的形式,这就包括了List一类。

注意看在构造HashMap的时候,传了一个整数值(Math.max((int) (c.size()/.75f) + 1, 16))进去,这个整数值实际上就是确定集合的初始容量。c.size表示容器大小,“.75f”表示平衡因子为0.75(不知道平衡因子的可以到网上再复习一下散列),c.size()/0.75就是一个散列(这里就是这个map)的理想存储大小。如果散列的理想存储大小不足16,那在初始化的时候就为map分16个单位的存储空间,即该集合初始容量为16。

  1. 通过传入平衡因子和初始容量进行构造
public HashSet(int initialCapacity, float loadFactor) {
        map = new HashMap<>(initialCapacity, loadFactor);
    }

这个很明显,基本和上面的概念类似。

4.只传入初始容量进行构造

    public HashSet(int initialCapacity) {
        map = new HashMap<>(initialCapacity);
    }
  1. 采用另外一种map进行构造
    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }

这个基本不怎么使用

成员函数

跟集合相关操作的核心操作实际上只有8个,我看了下,这8个函数加一起的代码量可能都不超过三十行,举个例子:

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

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

这是蕴含判断操作和添加操作,实际上所有的操作都交由其成员变量map完成了,所以很简单,我就不一一介绍了,我想介绍的是为什么通过这几行简单的代码就能完全实现集合的特性呢?

事实上我们知道离散数学中定义的集合有三个特性:

  • 确定性。给定任意一个元素,可以知道该元素是否存在于这个集合中。
  • 互异性。集合中的任意元素都是唯一的,不存在两个相同的元素。
  • 无序性。集合中的元素是没有顺序的。

无序性就不说了,因为在HashSet中根本就没有提供任何随机访问的API或数组。说下前两个特性。我们知道HashMap是以键值对的形式存储的,而保证键值对存储有效性的关键点就在于key和value是一一映射的,所以可以保证HashSet中存储的对象是唯一的,同时由于HashMap自身携带了contains方法,所以确定性也就实现了。

其他

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

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

相关文章

  • Java集合之HashSet源码分析

    阅读目录 一、HashSet简介 二、HashSet源码分析 三、HashSet的应用示例代码

  • Java 集合类原理

    Java基础——HashMap源码分析 Java基础——HashSet源码分析 Java基础——HashTable...

  • Java容器类框架分析(5)HashSet源码分析

    概述 在分析HashSet源码前,先看看HashSet的继承关系 从上图可以看出,HashSet继承自Abstra...

  • HashSet实现原理分析(Java源码剖析)

    本文将深入讨论HashSet实现原理的源码细节。在分析源码之前,首先我们需要对HashSet有一个基本的理解。 H...

  • Java集合源码分析-TreeSet

    上篇文章分析完HashSet和LinkedHashSet的源码,我们清楚:HashSet是无序的、不重复的、允许最...

  • HashSet源码分析

    HashSet是基于HashMap实现的,底层采用HashMap来保存元素,本篇文章需要在HashMap的基础上进...

  • HashSet源码分析

    从源码中可以看出, HashSet实现的功能通过重用HashMap去实现对应的功能。但是和HashMap实现的接口...

  • HashSet源码分析

    HashSet HashSet概述### HashSet实现Set接口,由哈希表(实际上是一个HashMap实例)...

  • HashSet源码分析

    Set集合的最主要特性就是没有重复元素,HashSet是Set的一个字类,其内部基于HashMap实现 1. 成员...

  • HashSet源码分析

    源码来自jdk1.8 HashSet实现了Set接口 内部由一个HashMap实现 允许一个null值 不同步 a...

网友评论

    本文标题:HashSet源码分析

    本文链接:https://www.haomeiwen.com/subject/chtzjctx.html