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(); }
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);
输出:
null s2 s1 sw2 s21 s5 s3 s4 [null, s2, s1, sw2, s21, s5, s3, s4]
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); } }
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); }
还有一个包级私有的构造函数:
HashSet(int initialCapacity, float loadFactor, boolean dummy) { map = new LinkedHashMap<>(initialCapacity, loadFactor); }
这个构造函数就是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);
输出:
s2 s3 s4 s5 s1 null s21 sw2 [s2, s3, s4, s5, s1, null, s21, sw2]
允许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); } //其余省略 }
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);
运行结果:
s1 s2 s21 s3 s4 s5 sw2 [s1, s2, s21, s3, s4, s5, sw2]
不允许重复,不允许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);
输出结果:[jj:17, zzh:18, qq:19]
可以看到不是强制性要求TreeSet的键为null。
参考资料:
《Comparable与Comparator浅析 》
欢迎支持笔者的作品《深入理解Kafka: 核心设计与实践原理》和《RabbitMQ实战指南》,同时欢迎关注笔者的微信公众号:朱小厮的博客(ID: hiddenkafka)。
本文作者:
朱小厮
版权声明:
未经本人允许,不得擅自转载、修改或用于其它任何用途!