集合Set解析

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;
/**
 * Constructs a new, empty set that orders its elements according to
 * their {@linkplain Comparable natural ordering}.
 */
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;
    /**
     * Creates an empty set.
     */
    public CopyOnWriteArraySet() {
        al = new CopyOnWriteArrayList<E>();
    }
  • 写时复制的Set,基于CopyOnWriteArrayList实现
  • 由于CopyOnWriteArrayList已经实现写时复制,并且由于写时的写安全特性以及高效的读特性进行元素遍历,很容易拓展出Set的功能。
  • 可以看到,该Set的很多方法也是简单调用CopyOnWriteArrayList来做的,因此,知道了CopyOnWriteArrayList 就知道了该Set了。关于CopyOnWriteArrayList可以看一下之前的内容。
EnumSet
  • 这个就没什么好说的,专用于枚举方面的高效Set。
  • 该类里大部分是静态和抽象方法,静态方法根据枚举类型来创建其子类实例。RegularEnumSet(不大于64个枚举值的)或JumboEnumSet(超过64个枚举值)。
  • 由于底层一些方法是通过位运算,所以效率非常高。

你可能感兴趣的:(JAVA)