《Java源码解析》集合框架Set之HashSet

《Java源码分析》:HashSet

前面几篇把Map、List相关的的几个类看了下,当然Set接口相关的类也相当的重要,我们也用的比较多,因此,我们也需要了解下Set接口的两个实现类HashSet、TreeSet的源码。这篇博文是记录HashSet的源码分析。下篇博文就来记录TreeSet的源码分析。

说明:我们都知道HashSet集合是不允许重复元素的,因此这个类的利用什么来保证这个集合里面不会有重复的元素呢?结论是:HashSet是借助于HashMap的key不允许重复这个特性来实现的。想想这个思想确实比较奇妙,是吧,HashMap是操作键值对,而HashSet是操作HashMap的key完成相关操作,或者这么说,HashSet全部的操作是借助于HashMap经过某种封装得到的

下面来开始具体的分析,其实,如果我们了解HashMap的内部实现,HashSet的内部实现就相当的容易理解。

HashSet的继承结构

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

HashSet继承了AbstractSet抽象类,也实现了Set、Cloneable和Serializable接口。

或许你可能有这样的疑惑:AbstractSet实现了Set接口,HashSet既要继承AbstractSet还需要实现Set接口呢??

如果你看过HashMap的源码,HashMap的继承结构和HashSet类似,,为了方便比较,还是贴一下:

public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable 

在HashMap我们已经探讨了为什么AbstractMap实现了Map接口,HashMap又继承AbstractMap还要实现Map接口呢??

这个问题在HashMap源码分析中已经有了答案,因此,HashSet的继承结构为什么如此,我想原因应该是一样的,HashSet之所以还实现Set接口,在功能上没有任何作用,仅仅只是使结构体系更加清晰完整。

HashSet的属性和构造函数

从下面可以看出,HashSet有一个HashMap的引用,且有一个常量。

HashSet的构造函数,都是创建一个map实例对象。HashSet对象的默认大小与HashMap默认大小一样,是16,且大小只能是2的幂次方。

private transient HashMap<E,Object> map;

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

/**
 * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
 * default initial capacity (16) and load factor (0.75).
 */
public HashSet() {
    map = new HashMap<>();
}

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

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

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

/**
 * Constructs a new, empty linked hash set.  (This package private
 * constructor is only used by LinkedHashSet.) The backing
 * HashMap instance is a LinkedHashMap with the specified initial
 * capacity and the specified load factor.
 *
 */
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
    map = new LinkedHashMap<>(initialCapacity, loadFactor);
}

HashSet中常见的一些方法

3.1、 add(E e)

源码如下:

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

看到这个源码是不是很爽,太简单了哈,就是将要添加的元素作为map的key,常量PRESENT作为value保存在HashMap对象中即可。有一点需要我们注意的是:这个add方法有返回值,返回值有map的put方法决定,当map调用put(key,value)方法时,如果存在key后,则map的put方法会返回此key对应的oldValue。此时add就会返回false

3.2、 iterator()

HashSet并没有提供类似于get的方法,只提供的获取访问元素的迭代器对象。iterator方法也是委托给了map,获取map的key集合的迭代器。

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

3.3、 其它的方法

其它方法的源码如下,全部都是委托给HashMap来实现的,是不是很简单哈,这里就不再过多的进行分析。

public int size() {
    return map.size();
}


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

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

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

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

public void clear() {
    map.clear();
}

小结

确实比较简单哈,关于HashSet的内部实现我们只需要记住一点:是基于HashMap来实现的,即所有方法都是委托给HashMap对象来实现的;在使用方面,也只需要记住一点:所有Set集合对象存储的元素是不允许重复的哈

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值