相比于前面两篇文章写的关于ArrayList和LinkedList,Set集合与他们的不同之处在于,Set不允许数据重复。
Set类集合类库的简化图如下。
可见:Set实现了Collection和Iterator
几种常见的Set:
- Set(interface): 存入Set的每个元素都必须是唯一的,因为Set不保存重复元素。加入Set的元素必须定义equals() 方法以确保对象的唯一性。Set和Collection有完全一样的接口。Set接口不保证维护元素的次序。
- HashSet: 为快速查找而设计的Set,存入HashSet的元素必须定义hashCode()
- TreeSet:保持次序的Set,底层为树结构。使用它可以从Set中提取有序的序列。元素必须实现Comparable接口
- LinkedHashSet: 具有HashSet的查询速度,且内部使用链表维护元素的顺序(插入的顺序)。于是在使用迭代器遍历Set时,结果会按照元素插入的次序显示。元素也必须定义hashCode()方法
HashSet原码阅读总结
private transient HashMap<E,Object> map;
private static final Object PRESENT = new Object();
HashSet的底层数据存储靠的是HashMap,且只是用了HashMap的key,value统一使用一个Object对象来填充。
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一共提供了4个构造器,都是初始化了一个HashMap对象。
public Iterator<E> iterator() {
return map.keySet().iterator();
}
HashSet在遍历的时候,取回了HashMap的key,对Key进行遍历。而HashMap的value则都被用object填充了。
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
添加一个元素的时候,就是往map里面添加了一条记录,key添加的元素,value为统一的object对象
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
移除一个元素的时候,调用的是map的remove操作。
关于HashMap的操作原理,会在以后的博客中写上,因为我还没看到HashMap。哈哈哈哈哈哈
TreeSet原码阅读总结
private transient NavigableMap<E,Object> m;
TreeSet 底层基于NavigableMap 实现,跟HashMap一样,只使用了NavigableMap的key,value统一设置为null
TreeSet(NavigableMap<E,Object> m) {
this.m = m;
}
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);
}
TreeSet 提供了五个构造器,全用于初始化NavigableMap 实例
public boolean add(E e) {
return m.put(e, PRESENT)==null;
}
添加一个元素,使用的是 map的put方法
public boolean remove(Object o) {
return m.remove(o)==PRESENT;
}
删除一个元素,使用的是map 的remove 方法
其实Set的底层使用的都是Map,一些具体的细节,可以在今后的Map原码阅读中去研究。
LinkedHashSet原码阅读
LinkedHashSet 基于LinkedHashMap实现。它继承了HashSet,实现了Set
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);
}
LinkedHashSet 提供了四个构造器。这四个构造器都调用了HashSet的构造器,第三个参数为无效参数。
从第三个构造器可知,LinkedHashSet的默认长度为16,扩容因子默认为0.75。
本文中提及的TreeSet和LinkedHashSet如何确保有序和保持插入顺序,将会在写Map的文章中再描述。