Set的实现是很简单的,但是这个简单的基础是建立在对于Map关键是HashMap的理解
1、Set不能够方重复元素但是可以添加一个null(并不是所有的Set都可以例如TreeMap的就不行),这里应该是开启了HashMap的putVal方法中的后两个参数
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {}
2、Set接口对象存放的是无序的,但是取出的顺序是有序的(不是添加的顺序但是也是固定的)
因为继承了Conllection所以可以使用迭代器的方式来进行遍历,同时也可以使用增强for循环的方式来实现——增强for循环底层使用的就是迭代器相关的原理
3、Set接口对象不能通过索引的方式来获取
4、TreeSet不能够存在null而HashSet可以:
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
但是在TreeSet的背后实现的TreeMap中不能够空指针的这一段是这样实现的:
HashSet的底层实际上是HashMap:
public HashSet() {
map = new HashMap<>();
}
关于HashMap的相关知识就不再探讨,这里主要了解的是HashSet中对于Map的使用,以及是如何实现不重复元素的存储,在HashSet的add方法中调用的是map的put方法:
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
并且他传递进去的value都是一个固定的:
private static final Object PRESENT = new Object();
之后调用的就完全是HashMap的put方法,set的值都是存放在Key中,实际上在HashMap的结构中每一次value也是发生了改变的不过他们都是相同的值PRESENT。
TreeSet是可排序的,当我们使用无参构造器创建TreeSet时,仍然是无序的。但是我们可以通过使用带有比较器的构造函数来进行创建,这样我们创建出来的TreeSet集合就会是有序的了
public TreeSet(Comparator<? super E> comparator) {
this(new TreeMap<>(comparator));
}
在TreeMap中将构造函数中传递过来的比较器付给了一个类中的全局变量:
private final Comparator<? super K> comparator;
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}
这里面着重要理解的有TreeSet的数据结构与HashMap的数据结构有何不同、通过比较器可以发现我们通过穿进去适当的比较器是可以改变Set不重复的这个规则的,那么这个规则是如何起作用的?
TreeMap中简单使用了红黑树的结构,由于他没有使用Hash所以他对于树形的判断依靠的是比较器,这一点是可以通过代码看出来的:
这一部分在TreeSet中的体现就是如果要是没有发生冲突正常插入了那么程序是会放回一个null,经过set相关的方法处理就变成了true表示我们的元素插入成功,如果要是没有插入成功,则会返回元素的原有值,从而通过set 中的判断会返回一个false
public boolean add(E e) {
return m.put(e, PRESENT)==null;
}
了解了Collections的功能之后之后的使用我们可以直接查看他的API,从而来指导Collections有哪些能力: