欢迎支持笔者新作:《深入理解Kafka:核心设计与实践原理》和《RabbitMQ实战指南》,同时欢迎关注笔者的微信公众号:朱小厮的博客。
欢迎跳转到本文的原文链接:https://honeypps.com/java/java-collection-set/
Set概述
Set几乎都是内部用一个Map来实现, 因为Map里的KeySet就是一个Set,而value是假值,全部使用同一个Object。Set的特征也继承了那些内部Map实现的特征。
HashSet
1. 定义
package java.util;
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
{
private transient HashMap<E,Object> map;
private static final Object PRESENT = new Object();
public HashSet() {
map = new HashMap<>();
}
//其余省略
}
2. 概述
HashSet是基于HashMap来实现的,操作很简单,更像是对HashMap做了一次“封装”,而且只使用了HashMap的key来实现各种特性,而HashMap的value始终都是PRESENT。
HashSet不允许重复(HashMap的key不允许重复,如果出现重复就覆盖),允许null值,非线程安全。
罗列几个主要方法:
public Iterator<E> iterator() {
return map.keySet().iterator();
}
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();
}
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
3. 使用
案例1:
Set<String> set = new HashSet<>();
set.add("s2");
set.add("s3");
set.add("s4");
set.add("s2");
set.add("s5");
set.add("s1");
set.add(null);
set.add("s21");
set.add("sw2");
set.add("s2");
for(String i:set)
System.out.println(i);
System.out.println(set);
- 13
- 14
输出:
null
s2
s1
sw2
s21
s5
s3
s4
[null, s2, s1, sw2, s21, s5, s3, s4]
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
LinkedHashSet
1. 定义(整个LinkedHashSet的代码)
package java.util;
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);
}
public LinkedHashSet(int initialCapacity) {
super(initialCapacity, .75f, true);
}
public LinkedHashSet() {
super(16, .75f, true);
}
public LinkedHashSet(Collection<? extends E> c) {
super(Math.max(2*c.size(), 11), .75f, true);
addAll(c);
}
}
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
2. 概述
HashSet有4个共有的构造函数:
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);
}
- 13
还有一个包级私有的构造函数:
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}
- 1
- 2
- 3
这个构造函数就是LinkedHashSet的关键,整个LinkedHashSet的四个构造函数全部都是调用了HashSet中的这个包级私有的构造函数(构造函数中的dummy就是标记,无实用,为了实现方法的重载而已),也就是说LinkedHashSet就是基于LinkedHashMap实现的。
3. 使用
案例2:
Set<String> set = new LinkedHashSet<>();
set.add("s2");
set.add("s3");
set.add("s4");
set.add("s2");
set.add("s5");
set.add("s1");
set.add(null);
set.add("s21");
set.add("sw2");
set.add("s2");
for(String i:set)
System.out.println(i);
System.out.println(set);
- 13
- 14
输出:
s2
s3
s4
s5
s1
null
s21
sw2
[s2, s3, s4, s5, s1, null, s21, sw2]
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
允许null值,保留插入顺序,非线程安全。
TreeSet
1. 定义
package java.util;
public class TreeSet<E> extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, java.io.Serializable
{
private transient NavigableMap<E,Object> m;
private static final Object PRESENT = new Object();
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);
}
//其余省略
}
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
2. 概述
TreeSet的内部基于TreeMap实现,同样value永远为PRESENT.
案例3:
Set<String> set = new TreeSet<String>();
set.add("s2");
set.add("s3");
set.add("s4");
set.add("s2");
set.add("s5");
set.add("s1");
// set.add(null);
set.add("s21");
set.add("sw2");
set.add("s2");
for(String i:set)
System.out.println(i);
System.out.println(set);
- 13
- 14
运行结果:
s1
s2
s21
s3
s4
s5
sw2
[s1, s2, s21, s3, s4, s5, sw2]
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
不允许重复,不允许null值(如果有基于null的比较器,就可以允许为null),默认按升序排列。
案例4(其中的Person2详细见《Comparable与Comparator浅析》):
Set<Person2> set = new TreeSet<Person2>(new Comparator<Person2>(){
@Override
public int compare(Person2 o1, Person2 o2)
{
if(o1==null || o2==null)
return 0;
return o1.getAge()-o2.getAge();
}
});
Person2 p1 = new Person2("zzh",18);
Person2 p2 = new Person2("jj",17);
Person2 p3 = new Person2("qq",19);
Person2 p4 = new Person2(null,19);
set.add(p1);
set.add(p2);
set.add(p3);
set.add(p4);
System.out.println(set);
- 13
- 14
- 15
- 16
- 17
- 18
- 19
输出结果:[jj:17, zzh:18, qq:19]
可以看到不是强制性要求TreeSet的键为null。
参考资料:
欢迎跳转到本文的原文链接:https://honeypps.com/java/java-collection-set/
欢迎支持笔者新作:《深入理解Kafka:核心设计与实践原理》和《RabbitMQ实战指南》,同时欢迎关注笔者的微信公众号:朱小厮的博客。