HashSet、TreeSet、LinkedHashSet原理及线程安全的使用

本文详细解析了Java集合框架中的HashSet、TreeSet和LinkedHashSet的原理,包括它们的数据结构和特性。HashSet基于HashMap实现,不保证元素顺序且非线程安全;TreeSet采用红黑树,提供排序和多种操作;LinkedHashSet基于LinkedHashMap,保持插入顺序。同时,介绍了如何使用Collections工具类创建线程安全的Set。

一、原理

 

        1、HashSet

        hashset底层其实维护的是一个HashMap实例。底层数据结构是哈希表。综合了数组和链表的优点。其有以下等特点:

        (1) 不允许存放重复值的集合(通过hashCode()函数及equals()函数来判断是否重复)。

        (2)不能保证元素的排列顺序,顺序可能与添加顺序不同,顺序也可能发生变化 (内部维护的是hashmap,采用hash取模确定数组索引位置,然后在存入链表)。

        (3) HashSet是非线程安全的。

        (4) 集合元素值可以是nul。

    /**
    * 维护的hashMap实例
    */    
    private transient HashMap<E,Object> map;

    
    /**
     * 无参构造器,创建一个hashMap实例,默认的加载因子是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);
    }

    /**
     * 指定初始容量,加载因子为默认的0.75
     */
    public HashSet(int initialCapacity) {
        map = new HashMap<>(initialCapacity);
    }

    /**
     * 指定容量,指定加载因子,区分其他不同访问权限的构造函数
     */
    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }

note:如果需要把某个类的对象保存到HashSet集合中,重写这个类的equals方法和hashCode方法时,应尽量保证两个对象通过equals比较返回true时,他们的hashCode方法返回值也相等。如果equals比较不同的时候,尽量让hashcode的值不同,以提升插入的效率。

       2、TreeSet

        TreeSet是SortedSet接口的实现类,TreeSet可以确保集合元素处于排序状态。底层数据结构采用的是红黑树。默认排序从小到大。内部维护了一个NavigableMap<E,Object>,该map继承SortMap。

public class TreeSet<E> extends AbstractSet<E>
    implements NavigableSet<E>, Cloneable, java.io.Serializable
{
    /**
     * 该map继承SortMap
     */
    private transient NavigableMap<E,Object> m;

    // 
    private static final Object PRESENT = new Object();

    /**
     * 
     */
    TreeSet(NavigableMap<E,Object> m) {
        this.m = m;
    }

    /**
     * 
     */
    public TreeSet() {
        this(new TreeMap<E,Object>());
    }

    /**
     * 
     */
    public TreeSet(Comparator<? super E> comparator) {
        this(new TreeMap<>(comparator));
    }

    /**
     * 
     */
    public TreeSet(Collection<? extends E> c) {
        this();
        addAll(c);
    }

    /**
     * 
     */
    public TreeSet(SortedSet<E> s) {
        this(s.comparator());
        addAll(s);
    }
}

 相较HashSet,TreeSet中多有以下函数:

        (1)Comparator comparator():如果TreeSet采用了定制顺序,则该方法返回定制排序所使用的Comparator,如果TreeSet采用自然排序,则返回nul。
        (2)Object first():返回集合中的第一个元素。
        (3)Object last():返回集合中的最后一个元素。
        (4)Object lower(Object e):返回指定元素之前的元素。
        (5)Object higher(Object e):返回指定元素之后的元素。
        (6)SortedSet subSet(Object fromElement,Object toElement):返回此Set的子集合,含头不含尾。
        (7)SortedSet headSet(Object toElement):返回此Set的子集,由小于toElement的元素组成。
        (8)SortedSet tailSet(Object fromElement):返回此Set的子集,由大于fromElement的元素组成。

        3、LinkedHashSet

        linkedHashSet是hashSet的一个子类,是基于LinkedHashMap实现的。

/**
 * 继承HashSet
 */
public class LinkedHashSet<E>
    extends HashSet<E>
    implements Set<E>, Cloneable, java.io.Serializable {

    private static final long serialVersionUID = -2851667679971038690L;

    /**
     * 指定容量及加载因子的构造函数
     */
    public LinkedHashSet(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor, true);
    }

    /**
     * 指定容量的构造函数,其加载因子用默认的0.75
     */
    public LinkedHashSet(int initialCapacity) {
        super(initialCapacity, .75f, true);
    }

    /**
     * 无参构造函数,默认容量16,加载因子为0.75
     */
    public LinkedHashSet() {
        super(16, .75f, true);
    }

    /**
     * 包含指定集合的构造函数
     */
    public LinkedHashSet(Collection<? extends E> c) {
        super(Math.max(2*c.size(), 11), .75f, true);
        addAll(c);
    }

    /**
     * java8的可分割迭代器
     */
    @Override
    public Spliterator<E> spliterator() {
        return Spliterators.spliterator(this, Spliterator.DISTINCT | Spliterator.ORDERED);
    }
}

        上面便是LinkedHashSet的全部代码,这里面我们并没有发现LinkedHashMap的踪影,为何说LinkedHashSet是基于LinkedHashMap实现的呢?

        下面我们可以看下其构造函数中调用父类的构造函数。

    /**
     * 指定容量及加载因子的构造函数,其中dummy参数是用来区分其他的构造函数,实现重载。
     */
    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }

二、线程安全的使用

        使用Colletcions这个工具类syn方法类创建个线程安全的set.Set<String> synSet = Collections.synchronizedSet(new HashSet<>())。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值