Set 是一种去重的数据结构,经常可以使用到,而HashSet,LinkedHashSet, TreeSet是最常见的Set实现。
类图如下:
Set在Collection的基础上没有增加额外的方法,但是增加一些约束,一方面,元素不能重复,另一方面,添加的元素类型,有的不能为null,不能包含自己,添加不符合的元素的操作会抛出异常。
AbstractSet 只实现了equals, hashcode 和removeAll(); 因为AbstactCollection已经实现了一部分方法。
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Set))
return false;
Collection<?> c = (Collection<?>) o;
if (c.size() != size())
return false;
try {
return containsAll(c);
} catch (ClassCastException unused) {
return false;
} catch (NullPointerException unused) {
return false;
}
}
public int hashCode() {
int h = 0;
Iterator<E> i = iterator();
while (i.hasNext()) {
E obj = i.next();
if (obj != null)
h += obj.hashCode();
}
return h;
}
hashCode是把每个元素的Hashcode累加,equals要求元素数量相同,并且包含对方的所有元素。
HashSet
在查看HashMap的时候,可以知道HashMap的Key已经Set,并且底层是hash表,因此是不是可以把Key取出来直接就变成HashMap了, 但是HashMap的Key是在Entry里,不可以单独创建,因此要创建一个HashMap 才可以使用Key。因此HashSet的实现,就是委托HashMap来实现的,由于不适用Map的Value,Map的所有value都指向同一个预定义好的对象,节约空间。
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
{
static final long serialVersionUID = -5024744406713321676L;
private transient HashMap<E,Object> map;
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
/**
* Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
* default initial capacity (16) and load factor (0.75).
*/
public HashSet() {
map = new HashMap<>();
}
...
}
LinkedHashSet
和 HashSet类似,仅仅是把HashMap替换成LinkedHashMap。之前我们知道LinkedHashMap有两种顺序,即添加顺序和访问顺序,但是LinkedHashSet的构造函数中无法选择,所以使用的是添加顺序。
HashSet 和LinkedHashSet都允许添加null为元素。
TreeSet
最后看一下,TreeSet 补充一下类图,
TreeSet 使得用迭代器访问时,可以以指定顺序排列,顺序按照comparetor或者 Comparable。 在TreeMap中,三个视角中,其中KeySet返回的就是NavigableSet,即TreeMap.KeySet 已经实现了NavigableSet。 而这个Set的实现,采用的是委托给NavigableMap。
而KeySet 是已经包权限静态类,而且是final的,不可以继承,因此TreeSet可以采取委托KeySet,再手动构造一个TreeMap,再创建KeySet
TreeSet() {
NavigableMap map = new TreeMap();
NavigableSet set = new TreeMap.KeySet(map);
}
实际上,这两委托两层,不如直接模仿KeySet的写法,再写一遍,直接委托即可。
public TreeSet() {
this(new TreeMap<E,Object>());
}
所有的Value都设置成固定对象。
小节:
- HashSet,LinkedHash TreeSet 都是基于Map的三个对应实现的委托实现,只是用map的key,而不是用Map的Value。