共计 3877 个字符,预计需要花费 10 分钟才能阅读完成。
关注“Java 后端技术全栈”**
回复“面试”获取全套面试材料
Set 继承于 Collection 接口,是一个不容许呈现反复元素,并且无序的汇合,次要有 HashSet 和 TreeSet 两大实现类,另外 LinkedHashSet 也有肯定的应用频率。
在判断反复元素的时候,Set 汇合会调用 hashCode()和 equal()办法来实现。
类图 UML
Set 罕用办法
与 List 一样都是接口,Set 接口也提供了汇合操作的根本办法。Java 四大汇合之一,但与 List 不同的是,Set 还提供了 equals(Object o)和 hashCode(),供其子类重写,以实现对汇合中插入反复元素的解决;
1public interface Set<E> extends Collection<E> {
2 // 增加
3 boolean add(E e);
4 boolean addAll(Collection<? extends E> c);
5
6 // 删除
7 boolean remove(Object o);
8 boolean removeAll(Collection<?> c);
9 void clear();
10
11 // 长度
12 int size();
13 // 是否为空
14 boolean isEmpty();
15
16 // 是否蕴含
17 boolean contains(Object o);
18 boolean containsAll(Collection<?> c);
19 boolean retainAll(Collection<?> c);
20
21 // 获取 Set 汇合的迭代器:22 Iterator<E> iterator();
23
24 // 把汇合转换成数组
25 Object[] toArray();
26 <T> T[] toArray(T[] a);
27
28 // 判断元素是否反复,为子类进步重写办法
29 boolean equals(Object o);
30 int hashCode();
31}
接口里常识定义了办法,具体的实现请看上面两个罕用实现类。
HashSet
HashSet 是用来存储 没有反复元素的汇合类 , 并且是无序的 。实现了 Set 接口。底层其实次要是应用 HashMap 机制实现,所以也是 线程不平安。
局部源码:
1public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable{
2 private transient HashMap<E,Object> map;
3 // 这里这个 PRESENT 就是作为 HashMap 中的 key
4 private static final Object PRESENT = new Object();
5 public HashSet() {6 map = new HashMap<>();
7 }
特色:
- 不可反复
- 无序
- 线程不平安,若多个线程同时操作 HashSet,必须通过代码实现同步;
- 汇合元素能够是 null,但只能放入一个 null
应用场景:去重、不要求程序
原理:HashSet 底层由 HashMap 实现,插入的元素被当做是 HashMap 的 key,依据 hashCode 值来确定汇合中的地位,因为 Set 汇合中并没有角标的概念,所以并没有像 List 一样提供 get()办法。当获取 HashSet 中某个元素时,只能通过遍历汇合的形式进行 equals()比拟来实现;
罕用办法
1 // 增加
2 public boolean add(E e) {3 return map.put(e, PRESENT)==null;
4 }
5 // 移除
6 public boolean remove(Object o) {7 return map.remove(o)==PRESENT;
8 }
9 // 清空
10 public void clear() {11 map.clear();
12 }
13 // 获取迭代器
14 public Iterator<E> iterator() {15 return map.keySet().iterator();
16 }
17 // 判断是否为空
18 public boolean isEmpty() {19 return map.isEmpty();
20 }
21 // 求汇合大小
22 public int size() {23 return map.size();
24 }
正如下面所说,底层应用 HashMap 的 key 不能反复机制来实现没有反复的 HashSet。
TreeSet
TreeSet 实现了 SortedSet 接口,意味着能够排序,它是一个 有序 并且 没有反复的汇合类 ,底层是通过 TreeMap 实现。TreeSet 并不是依据插入的程序来排序,而是字典天然排序。 线程不平安 。从名字上能够看出,此汇合的实现和树结构无关。与 HashSet 汇合相似,TreeSet 也是基于 Map 来实现,具体实现TreeMap(前面解说),其底层构造为 红黑树(非凡的二叉查找树)。
TreeSet 反对两种排序形式:天然升序排序 和自定义排序。
局部源码
1public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>, Cloneable, java.io.Serializable{
2
3 private transient NavigableMap<E,Object> m;
4
5 private static final Object PRESENT = new Object();
6
7 TreeSet(NavigableMap<E,Object> m) {
8 this.m = m;
9 }
10
11 public TreeSet() {12 this(new TreeMap<E,Object>());
13 }
罕用办法
`1 // 求汇合大小
2 public int size() {3 return m.size();
4 }
5 // 判断是否为空
6 public boolean isEmpty() {7 return m.isEmpty();
8 }
9 // 判断是否蕴含
10 public boolean contains(Object o) {11 return m.containsKey(o);
12`
特色:
- 不可反复
- 有序,默认天然升序排序
- 线程不平安,若多个线程同时操作 HashSet,必须通过代码实现同步;
- 汇合元素不能够为 null
- 对插入的元素进行排序,是一个有序的汇合(次要与 HashSet 的区别);
- 底层应用红黑树结构,而不是哈希表构造;
原理:
TreeSet 底层是基于 treeMap(红黑树结构)实现的,能够自定义比拟器对元素进行排序,或是应用元素的天然程序。
应用场景:去重、要求排序
LinkedHashSet
LinkedHashSet 是应用 HashSet 机制实现,它是一个能够 保障插入程序或是拜访程序 ,并且 没有反复 的汇合类。线程不平安。
数据结构:数组 + 双向链表,Entry 构造:before|hash|key|value|next|after,before 和 after 用于保护整个双向链表。
局部源码
1public class LinkedHashSet<E> extends HashSet<E> implements Set<E>, Cloneable, java.io.Serializable {
2
3 public LinkedHashSet(int initialCapacity, float loadFactor) {4 super(initialCapacity, loadFactor, true);
5 }
6
7 public LinkedHashSet(int initialCapacity) {8 super(initialCapacity, .75f, true);
9 }
10
11 public LinkedHashSet() {12 super(16, .75f, true);
13 }
罕用办法
从这里能够看出,这家伙基本上都是应用 HashSet 来实现的,所以罕用办法和 HashSet 雷同。
特色:
- 汇合元素不能够为 null;
- 线程不平安。
原理:
LinkedHashSet 底层应用了 LinkedHashMap 机制(比方 before 和 after),加上又继承了 HashSet,所以能够实现既能够保障迭代程序,又能够达到不呈现反复元素。
应用场景:去重、须要保障插入或者拜访程序
HashSet、TreeSet、LinkedHashSet 的区别
- HashSet 只去重
- TreeSet 去重并排序
- LinkedHashSet 去重并保障迭代程序。