Java集合-HashSet源码实现分析

2017-08-29  本文已影响143人  Misout

概要

阅读本文前,请先阅读笔者写的文章:Java集合-HashMap源码实现深入解析
理解了HashMap,再来理解HashSet就非常轻松了。

HashSet特性

来看下HashSet源码中的英文说明:

This class implements the Set interface, backed by a hash table
(actually a HashMap instance). It makes no guarantees as to the
iteration order of the set; in particular, it does not guarantee that the
order will remain constant over time. This class permits the null
element.

从英文说明中看出几个关键点:

1、实现了Set接口。
2、内部维护的一个HashMap实例来保存数据。也就是对HashMap做了一层封装而已。
3、不保证元素的插入顺序和迭代时的顺序一致,也不保证元素内部的顺序始终不变。
4、允许存null。
5、隐藏特性:set中的元素不会重复。因为HashMap的key就是Set的元素,key是不允许重复的。

所以,从这些信息就能够清晰的知道,HashSet完全是对HashMap的一层封装,内部的所有方法都是调用HashMap的方法来实现,自然也就是非线程安全的。

类及其数据结构

HashSet的类及属性源码如下,仅仅只有两个属性:map和PRESENT。数据存储完全采用map,PRESENT知识个占位对象,用于add元素时作为map中value的虚拟值,即put(e, PRESENT),后面看到源码就能明白。

public class HashSet<E> extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable
{
    private transient HashMap<E,Object> map;
    
    // 一个虚拟值,用于添加元素到map中时,key为元素的值,value用此虚拟值代替
    private static final Object PRESENT = new Object();
}

HashSet继承自AbstractSet,并实现Set接口。

五个构造方法

    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) {
        map = new HashMap<>(initialCapacity);
    }
    
    public HashSet(int initialCapacity, float loadFactor) {
        map = new HashMap<>(initialCapacity, loadFactor);
    }
    // dummy没有任何用处,此构造方法也提供给LinkedHashSet初始化
    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }

五个构造方法中,可以看到除了第5个用的LinkedHashMap外,其他四个都用的HashMap,都是简单的new了一个HashMap或LinkedHashMap来初始化。第5个构造方法主要提供给LinkedHashSet,因为LinkedHashSet继承自HashSet,内部的构造方法全部调用的是HashSet的第5个构造方法,即LinkedHashSet底层采用LinkedHashMap来实现。

基本方法

add方法把存入的元素自身当做key,value用PRESENT填充无实际意义。所有的基本操作用的都是map方法来实现,所以理解了HashMap,HashSet就小菜一碟啦。

public boolean add(E e) {
    // 用前面提到的PRESENT对象填充元素的value值
    return map.put(e, PRESENT)==null;
}

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

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

public int size() {
    return map.size();
}
    
public boolean isEmpty() {
    return map.isEmpty();
}
    
public void clear() {
    map.clear();
}
上一篇 下一篇

猜你喜欢

热点阅读