Set是一个常见接口,用来保存不同的元素。常用的实现了Set接口的类有HashSet,TreeSet。一个底层基于HashMap实现,另一个基于TreeMap实现,理解了上述两个Map之后分析Set源码就比较简单了。
HashSet源码分析
hashset 基于HashMap实现,把传入的值作为HashMap的key,由于底层HashMap的key是不可能重复的,因此HashSet也是不会重复的,即其中包含的所有元素都不重复,只保存一份。
重要参数及构造函数
private transient HashMap<E,Object> map;//底层是一个HashMap
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
/**
* 构造一个默认的HashSet,底层的hashMap容量是16,负载因子是0.75
*/
public HashSet() {
map = new HashMap<>();
}
/**
* 构造一个指定集合的HashSet,底层的HashMap容量是集合大小/0.75和默认容量的最大值
* 然后把集合的值放入HashSet中
* @param c the collection whose elements are to be placed into this set
* @throws NullPointerException if the specified collection is null
*/
public HashSet(Collection<? extends E> c) {
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
/**
* 使用所给的容量和构造因子构造一个HashMap
*
* @param initialCapacity the initial capacity of the hash map
* @param loadFactor the load factor of the hash map
* @throws IllegalArgumentException if the initial capacity is less
* than zero, or if the load factor is nonpositive
*/
public HashSet(int initialCapacity, float loadFactor) {
map = new HashMap<>(initialCapacity, loadFactor);
}
/**
* 只包含容量,负载因子默认0.75
*
* @param initialCapacity the initial capacity of the hash table
* @throws IllegalArgumentException if the initial capacity is less
* than zero
*/
public HashSet(int initialCapacity) {
map = new HashMap<>(initialCapacity);
}
/**
* 使用LinkedHashMap来实现底层。
*
* @param initialCapacity the initial capacity of the hash map
* @param loadFactor the load factor of the hash map
* @param dummy ignored (distinguishes this
* constructor from other int, float constructor.)
* @throws IllegalArgumentException if the initial capacity is less
* than zero, or if the load factor is nonpositive
*/
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}
HashSet 的重要方法。HashSet方法不多。
/**
* 返回底层map的大小
*
* @return the number of elements in this set (its cardinality)
*/
public int size() {
return map.size();
}
/**
* 向Set中添加一个元素,这里调用map的put方法,由于放入的元素当做key值,所有的value都是固定的,因此HashMap的底层数组上
*始终都只有一个元素,不会产生链表。相当于把key值算出hash值后放在对应的桶中。
* @param e element to be added to this set
* @return <tt>true</tt> if this set did not already contain the specified
* element
*/
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
/**
* 删除一个元素,调用map的remove方法,找到对应的key值然后删除即可。
* @param o object to be removed from this set, if present
* @return <tt>true</tt> if the set contained the specified element
*/
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
/**
* 调用map的remove方法清除。
* The set will be empty after this call returns.
*/
public void clear() {
map.clear();
}
/**
* 返回一个浅复制对象。
*
* @return a shallow copy of this set
*/
@SuppressWarnings("unchecked")
public Object clone() {
try {
HashSet<E> newSet = (HashSet<E>) super.clone();
newSet.map = (HashMap<E, Object>) map.clone();
return newSet;
} catch (CloneNotSupportedException e) {
throw new InternalError(e);
}
}
TreeSet源码分析
一些参数和构造方法。
/**
* 底层map
*/
private transient NavigableMap<E,Object> m;
// 用作TreeMap中插入的value值,都是一样的保证hashcode相等
private static final Object PRESENT = new Object();
/**
* 使用map构造
*/
TreeSet(NavigableMap<E,Object> m) {
this.m = m;
}
/**
* 构造一个自然排序的TreeMap,并调用上一个构造方法,传入值也就是新的TreeMap。
*/
public TreeSet() {
this(new TreeMap<E,Object>());
}
/**
* 传入一个比较器,并且构造一个TreeMap调用上面的构造方法。
*/
public TreeSet(Comparator<? super E> comparator) {
this(new TreeMap<>(comparator));
}
/**
* 使用一个集合作为参数,然后调用addall方法把集合添加到treeset中。
*/
public TreeSet(Collection<? extends E> c) {
this();
addAll(c);
}
/**
* 构造一个包含相同元素的新树集合,使用指定的排序集合进行相同的排序。
*
* @param s sorted set whose elements will comprise the new set
* @throws NullPointerException if the specified sorted set is null
*/
public TreeSet(SortedSet<E> s) {
this(s.comparator());
addAll(s);
}
TreeSet中的重要方法。整体上都是调用Treemap 的方法,比较简单。
/**
* 将set逆序输出,调用addAll来添加一个新的逆序map。
*/
public NavigableSet<E> descendingSet() {
return new TreeSet<>(m.descendingMap());
}
/**
* 返回size
*
* @return the number of elements in this set (its cardinality)
*/
public int size() {
return m.size();
}
/**
* 判断是否为空
* @return {@code true} if this set contains no elements
*/
public boolean isEmpty() {
return m.isEmpty();
}
/**
* Returns {@code true} if this set contains the specified element.
* More formally, returns {@code true} if and only if this set
* contains an element {@code e} such that
* <tt>(o==null ? e==null : o.equals(e))</tt>.
*
* @param o object to be checked for containment in this set
* @return {@code true} if this set contains the specified element
* @throws ClassCastException if the specified object cannot be compared
* with the elements currently in the set
* @throws NullPointerException if the specified element is null
* and this set uses natural ordering, or its comparator
* does not permit null elements
*/
public boolean contains(Object o) {
return m.containsKey(o);
}
/**
* 将元素作为key通过TreeMap方法添加到树中。
*/
public boolean add(E e) {
return m.put(e, PRESENT)==null;
}
/**
* 调用TreeMap底层remove方法删除元素,若存在则和PRESEET对比,肯定返回true,否则的话若元素不存在则返回false。
*/
public boolean remove(Object o) {
return m.remove(o)==PRESENT;
}
/**
* 删除所有元素。
*/
public void clear() {
m.clear();
}
/**
*将指定集合中的所有元素添加到这个set集合中去。
*
*/
public boolean addAll(Collection<? extends E> c) {
// Use linear-time version if applicable
if (m.size()==0 && c.size() > 0 &&
c instanceof SortedSet &&
m instanceof TreeMap) {//传入的参数是一个排序map,底层要是一个TreeMap。
SortedSet<? extends E> set = (SortedSet<? extends E>) c;
TreeMap<E,Object> map = (TreeMap<E, Object>) m;//强制类型转换
Comparator<?> cc = set.comparator();
Comparator<? super E> mc = map.comparator();
if (cc==mc || (cc != null && cc.equals(mc))) {
map.addAllForTreeSet(set, PRESENT);
return true;
}
}
return super.addAll(c);
}
TreeSet中的一些查看和删除方法。
/**
* 查找排序树上第一个的元素(一般类似于最左子树这样)
*/
public E first() {
return m.firstKey();
}
/**
* 查找排序树上最后一个元素
*/
public E last() {
return m.lastKey();
}
// NavigableSet API methods
/**
*小于e的最大的key。
*/
public E floor(E e) {
return m.floorKey(e);
}
/**
*大于e的最小的key
* @since 1.6
*/
public E ceiling(E e) {
return m.ceilingKey(e);
}
/**
* 删除并返回第一个元素
*/
public E pollFirst() {
Map.Entry<E,?> e = m.pollFirstEntry();
return (e == null) ? null : e.getKey();
}
/**
* 删除并返回最后一个元素。
*/
public E pollLast() {
Map.Entry<E,?> e = m.pollLastEntry();
return (e == null) ? null : e.getKey();
}
总结
整体而言set实在是比较简单的,都是调用map里的方法,重要的思想也都在map中有所体现,这里就不过多赘述了。