java Set 的一些总结

时间:2022-06-28 19:40:33
最近学习了一下java set 总结如下:
1. set接口 : 此接口定义了一个不包含重复元素的 collection接口,set定义了不包含满足 e1.equals(e2) 的元素,并且最多包含一个null元素。此接口的实现类引入了不同的因素影响着 e1.equals(e2) 的结果,所以具体涉及到的影响因子要是具体的实现类而定。
2. 实现类HashSet: 
这个实现类实现了 set接口,set接口定义 集合中不包含 e1.equals(e2)的元素,但我们知道HashSet 要保存不同的值,则需要重写其 hashCode方法和 equals方法。这个和HashSet的内部实现原理密切相关的,在HashSet的内部用了一个HashMap来存储元素。
在HashSet的 JDK接口中添加方法值这样定义的:public boolean add(E e) {     return map.put(e, PRESENT)== null;}// Dummy value to associate with an Object in the backing Map    private static final Object PRESENT = new Object();PRESENT 是一个虚假的值,其实其对象存在了HashMap的key上。那我们在来看HashMap的存储的这段代码:java Set 的一些总结
java Set 的一些总结java Set 的一些总结
画红框的这段代码就觉得了我们在用HashSet 在存储对象的时候必须要重写其hashcode 方法及 equals方法。如果HashCode 和equals都不相等或其中一个不想等会怎么存储呢?先说结论然后我们在研究一下其实现的代码:     如果equals相同,hashCode不同,两个相同的值的对象存储在不同的位置; 如果hashCode相同,equals 不同,则会采用链式接口将2个对象存储在同一个位置。这种开散列的方法想必我们大学里都学过了。但是具体的实现是怎么样的呢,我们还是回过头来看HeshMap(因为HashSet测存储用的就是HashMap) 我们看一下上面那段代码。 如果画红框的那个条件校验失败,则会调用 addEntry 方法,那其实现就在这个addEntry方法里了。addEntry的定义如下:java Set 的一些总结
java Set 的一些总结
Entry的构造函数如下:java Set 的一些总结
java Set 的一些总结
由此可见Entry就是一个链式存储结构,当table[bucketIndex] 这个槽为空的时候 e就是空值,那么传入的这个对象就占用了这个槽,如果table[bucketIndex] 不为空的时候就新添加的元素就会以开散列的方式以链表的方式挂在前一个对象的后面。 ps: 这里需要注意的一点就是 bucketIndex 是通过hashCode算出来
3.LinkedHashSet    LinkedHashSet是HashSet的子类,相同点是它继承了set的基因就是它的元素不能有重复的,但是它和HashSet的不同是因为它是有序的,它以一个链表的方式维护着元素的顺序。    那么它到底是怎么是实现的呢,那我们还是一起来分析一下JDK的代码:    LinkedHashSet 的实现就只是写了几个构造函数,但是这个构造函数却让LinkedHashSet 和HashSet有了很大的不同,HashSet 的下层存储采用的是HashMap,但LinkedHashSet通过定义的构造函数将底层的存储容器换成了LinkedHashMap,由此可见这个集合有序性的还是靠底层的存储LinkedHashMap来保证的。那就让我们来看一下LinkeHashMap的代码。   LinkedHashMap 继承自HashMap,不同点是他维护了一个Entry的双重链表。可以说LinkedHashMap扬长避短的发挥了哈希表和链表的特点。
java Set 的一些总结

这里维护了一个链表的header。当向LinkedHashMap中插入一个元素的时候,方法继承自HashMap

java Set 的一些总结
java Set 的一些总结
  其不同的部分是LinkedHashMap中定义的Entry对象重写了recordAccess方法,通过这个recordAccess方法实现了链表的创建。    LinkedHashMap 定义的Entry关键代码如下所示:java Set 的一些总结
java Set 的一些总结
画红框的部分则实现了双重链表的实现逻辑。     再来看LinkedHashMap当中实现的迭代器类的实现便利的就是其维护的双重链表,这就保证了其存储的元素的有序性。     核心代码如下:java Set 的一些总结
 java Set 的一些总结4. SortedSet    这个接口定义了一个关于元素总体排序的Set。 这个接口要求加入到这个容器的每个元素都是可比较的。这接口提供的方法感觉可操作性更强,提供的比较能力更强。
5. TreeSet     这个接口使用compareTo方法对所有元素进行比较,从Set的观点来看,此方法认为相等的两个元素就是相等的。即使Set的顺序与equals不一致。简单来说这个接口违背了Set接口的常规协定,其实两个对象equals比较的对象不一样,但只要Comparator比较出来的值一样那也认为是一样的元素。总结一下, TreeSet 采用排序的存储结构是红黑树,支持2种排序方式:自然排序和定制排序。自然排序,元素需要实现Comparable接口或者为元素指定比较器,元素需要为比较器所接受。     TreeSet底层存储采用了是TreeMap,所以TreeMap的底层红黑树的存储结构就决定TreeSet的存储结构。     TreeMap的节点定义关键代码是:
java Set 的一些总结
java Set 的一些总结
java Set 的一些总结
java Set 的一些总结
以上这段代码标红框的部分就决定了元素要么实现Comparable接口 要么定义Comparator。          PS:以上提到的几种set都是现成不安全的Set类型。如果需要使用线程安全的Set,则需要对其进行包装一层。     例如:  SortedSet s = Collections.synchronizedSortedSet(new TreeSet(...));       Set s = Collections.synchronizedSet(new HashSet(...));      以上是抽个空学习总结了一下 set的知识,有什么不对的地方,请大家多多指正!