Set
Set体现的是数学上的集合的概念,集合内没有相同的元素,这也是与List主要的区别。 另一方面,由于java的Map的key就是没有重复的,所以很多情况下,Set的内部实现就是其相应的Map,把Map的key作为Set,value存为null或Object,例如HashSet内部是HashMap, TreeSet内部也是TreeMap。
HashSet
private transient HashMap< E, Object> map;
private static final Object PRESENT = new Object ( ) ;
public HashSet ( ) {
map = new HashMap < > ( ) ;
}
public boolean isEmpty ( ) {
return map. isEmpty ( ) ;
}
public int size ( ) {
return map. size ( ) ;
}
从上面源码可以看到,HashSet的内部就是HashMap, 其isEmpty、size、add
等方法都是简单的调用了内部map的对应方法。 所以HashSet的特性和HasMap很相似,无序,线程不安全等等,详情可以看前面HashMap相关的内容。 P.S 从中我们也可以知道,hashCode对Set的性能也很重要,从HasMap的put也可以发现,和HashMap一样,在错误 的hashCode方法下HashSet是有可能放“相同”元素的,这种代码是错误的,只是说明一下有这种情况。如下:
static class TestObject {
@Override
public int hashCode ( ) {
return new Random ( ) . nextInt ( 5 ) ;
}
}
public static void main ( String[ ] args) {
HashSet< Object> set = new HashSet < > ( ) ;
Object obj = new TestObject ( ) ;
set. add ( obj) ;
set. add ( obj) ;
set. add ( obj) ;
set. add ( obj) ;
System. out. println ( set. size ( ) ) ;
System. out. println ( set. toString ( ) ) ;
}
TreeSet
private transient NavigableMap< E, Object> m;
private static final Object PRESENT = new Object ( ) ;
public TreeSet ( ) {
this ( new TreeMap < E, Object> ( ) ) ;
}
public boolean isEmpty ( ) {
return m. isEmpty ( ) ;
}
可以看到,TreeSet的内部就是TreeMap, 其isEmpty、size、add
也是简单的调用了内部map的对应方法。 所以TreeSet和TreeMap的特性也基本一致的。
LinkedHashSet
public class LinkedHashSet < E> extends HashSet < E>
implements Set < E> , Cloneable, java. io. Serializable { }
public LinkedHashSet ( int initialCapacity, float loadFactor) {
super ( initialCapacity, loadFactor, true ) ;
}
可以看到,LinkedHashSet继承了HashSet,而在构造的时候,调用了一个HashSet的default级别的构造函数。
HashSet ( int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap < > ( initialCapacity, loadFactor) ;
}
该构造函数不对外(因此外部使用的HashSet还是HashMap的结构),只有LinkedHashSet使用了。 可以看到,该构造函数,将内部map指向新创建LinkedHashMap。 所以,LinkedHashSet底层是 LinkedHashMap实现的。
ConcurrentSkpListSet
private final ConcurrentNavigableMap< E, Object> m;
public ConcurrentSkipListSet ( ) {
m = new ConcurrentSkipListMap < E, Object> ( ) ;
}
该Set也是基于Map实现的,看ConcurrentSkipListMap的属性即可。 ConcurrentSkipListMap是基于跳表 实现的线程安全的Map 需要注意的是,由于底层Map不支持value为null ,因此add
时的value都用了Boolean.TRUE
,即所有的value都指向同一个小对象Boolean.TRUE
,从而减小空间损耗
CopyOnWriteArraySet
private final CopyOnWriteArrayList< E> al;
public CopyOnWriteArraySet ( ) {
al = new CopyOnWriteArrayList < E> ( ) ;
}
写时复制的Set,基于CopyOnWriteArrayList实现 由于CopyOnWriteArrayList已经实现写时复制,并且由于写时的写安全特性以及高效的读特性进行元素遍历,很容易拓展出Set的功能。 可以看到,该Set的很多方法也是简单调用CopyOnWriteArrayList来做的,因此,知道了CopyOnWriteArrayList 就知道了该Set了。关于CopyOnWriteArrayList可以看一下之前的内容。
EnumSet
这个就没什么好说的,专用于枚举方面的高效Set。 该类里大部分是静态和抽象方法,静态方法根据枚举类型来创建其子类实例。RegularEnumSet(不大于64个枚举值的)或JumboEnumSet(超过64个枚举值)。 由于底层一些方法是通过位运算,所以效率非常高。