HashSet源码分析

一、HashSet概述

(1)HashSet实现Set接口,底层基于HashMap实现,但与HashMap不同在于HashMap存储键值对,HashSet仅存储对象——key

(2)HashSet使用成员对象来计算hashcode值

(3)HashSet的特点

  • 无序性
  • 唯一性(允许使用null)
  • 本质上讲就是HashMap
  • HashSet没有提供get方法,和HashMap一样,因为Set内部是无序的,只能通过迭代的方式获得

二、源码分析

1、继承结构

public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.SerializableP{}

(1)继承

  • AbstractSet:与ArrayList和LinkedList一样,贼他们的抽象父类中,都提供了equals方法和hashCode方法,本身并不实现这两个方法

AbstractSet中的equals()

public boolean equals(Object o) {
    if (o == this)
        return true;

    if (!(o instanceof Set))//o没有实现Set接口
        return false;//返回false
    Collection<?> c = (Collection<?>) o;//向下转换
    if (c.size() != size())//元素个数不相等
        return false;//返回false
    try {
        return containsAll(c);//组个判断集合是否包含其他元素
    } catch (ClassCastException unused)   {
        return false;
    } catch (NullPointerException unused) {
        return false;
    }
}

(2)接口

  • Serializable接口,表明它支持序列化
  • Cloneable接口,表明它支持克隆,可以调用超类的clone()方法进行浅拷贝

2、属性

//版本号
static final long serialVersionUID = -5024744406713321676L;
//底层使用HashMap存储数据
private transient HashMap<E,Object> map;
//用来填充底层数据结构HashMap中的value,因为HashSet只用key存储数据
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();

3、构造器

(1)无参构造器

构建了一个空的set集合,其底层的HashMap实例使用默认的初始容量(16)和加载因子(0.75)

public HashSet() {
    map = new HashMap<>();//实例化了一个HashMap
}

(2)HashSet(Collection<? extends E> c)

使用HashMap(int)构造器,创建了一个新的HashSet

初始容量由对象c的临界值和默认初始化容量的最大值决定

临界值 = 数组长度容量 * 负载因子

public HashSet(Collection<? extends E> c) {
    map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
    addAll(c);
}

AbstractCollection类中的addAll方法

public boolean addAll(Collection<? extends E> c) {
    boolean modified = false;//失败返回false
    for (E e : c)
        if (add(e))//添加成功
            modified = true;//返回true
    return modified;
}

(3)HashSet(int initialCapacity)

使用HashMap(int)构造器,创建了一个新的HashSet

初始化容量为initialCapacity

public HashSet(int initialCapacity) {
    map = new HashMap<>(initialCapacity);
}

(4)HashSet((int initialCapacity, float loadFactor)

使用HashMap(int, float)构造器,创建了一个新的HashSet

初始化容量为initialCapacity

负载因子为loadFactor

public HashSet(int initialCapacity, float loadFactor) {
    map = new HashMap<>(initialCapacity, loadFactor);
}

4、方法

4.1 add(E):boolean

添加一个新的元素

调用HashMap的put(Key, Value):V方法,进行添加,添加成功返回null,覆盖返回旧值

PRESENT用来填充底层数据结构HashMap中的value,因为HashSet只用key存储数据

public boolean add(E e) {
    return map.put(e, PRESENT)==null;//动态绑定,执行HashMap的put方法
}

4.2 remove(Object):boolean

删除一个元素

调用HashMap的remove(Object):V的方法,移除成功返回被删除的键对应的值,删除失败返回null

HashSet底层HashMap中所有键值对的值都是PRESENT

如果remove返回PRESENT说明已存在该元素,且删除成功

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

4.3 size():int

返回HashMap中元素的个数

public int size() {
    return size;
}

4.4 contains(Object):boolean

调用HashMap的containsKey()方法,判断HashMap中是否包含某个key

这里检查set中是否包含某个元素

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

4.5 isEmpty():boolean

调用HashMap的isEmpty方法,判断HashMap是否为空

public boolean isEmpty() {
    return map.isEmpty();
}

4.6 iterator():iterator e

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

三、总结

(1)①HashSet内部通过使用HashMap的键来存储集合中的元素,而且内部的HashMap的所有值都是null。(因为在为HashSet添加元素的时候,内部HashMap的值都PRESENT),而PRESENT在实例域的地方直接初始化了,而且不允许改变

(2)hashSet底层是基于hashMap实现的

(3)hashSet存储的元素对应hashMap的key,因为hashMap不能存储重复的Key,所以hashSet不能存放重复元素,HashMap中的key不可重复,重复的key会覆盖其value

(4)由于hashMap的key是基于hashCode存储对象的,所以hashSet中存放的对象也是无序的

(5)hashSet也没有提供get方法,可以通过Iterator迭代器获取数据

posted @ 2021-12-26 15:42  DarkSki  阅读(98)  评论(0编辑  收藏  举报