Set

Hashset

插入无序,不可指定位置访问,不能重复

1.如果没有其他的限制,这就是默认的选择,比较常用

private transient HashMap<E,Object> map;
//默认构造器
public HashSet() {
    map = new HashMap<>();
}
//将传入的集合添加到HashSet的构造器
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就是一个皮包公司,它就对外接活儿,活儿接到了就直接扔给HashMap处理了。因为底层是通过HashMap实现的,这里简单提一下:

HashMap的数据存储是通过数组+链表/红黑树实现的,存储大概流程是通过hash函数计算在数组中存储的位置,如果该位置已经有值了,判断key是否相同,相同则覆盖,不相同则放到元素对应的链表中,如果链表长度大于8,就转化为红黑树,如果容量不够,则需扩容(注:这只是大致流程)。

2.add

HashSet的add方法时通过HashMap的put方法实现的,不过HashMap是key-value键值对,而HashSet是集合,那么是怎么存储的呢,我们看一下源码

private static final Object PRESENT = new Object();

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

看源码我们知道,HashSet添加的元素是存放在HashMap的key位置上,而value取了默认常量PRESENT,是一个空对象,至于map的put方法,大家可以看HashMap原理(二) 扩容机制及存取原理

之后的remove方法也是通过hashmap进行的

TreeSet

1.底层用treemap实现,实现了sortedset接口。红黑树实现,插入无序内部有序。可以自然和定制排序

2.TreeSet中的元素必须实现Comparable接口并重写compareTo()方法,TreeSet判断元素是否重复 、以及确定元素的顺序 靠的都是这个方法; 

  ①对于java类库中定义的类,TreeSet可以直接对其进行存储,如String,Integer等,因为这些类已经实现了Comparable接口; 
  ②对于自定义类,如果不做适当的处理,TreeSet中只能存储一个该类型的对象实例,否则无法判断是否重复。 
3.相对HashSet,TreeSet的优势是有序,劣势是相对读取慢,所以要根据不同的场景选择不同的集合。

LinkedHashSet

1.具有可预知迭代顺序的 Set 接口的哈希表和链接列表实现。
  * 此实现与 HashSet 的不同之外在于,后者维护着一个运行于所有条目的双重链接列表。
  * 父类:HashSet
  * 父接口:Set
  * 注意,此实现不是同步的
 
 
 
 
 
 
原文地址:https://www.cnblogs.com/QianYue111/p/13322026.html