当前位置:   article > 正文

TreeSet解析

treeset

目录

一、TreeSet介绍

二、源码分析

1、TreeSet实现的接口

2、TreeSet中的变量

3、TreeSet的构造方法

(1)同包下的构造方法

(2)无参构造方法

(3)带比较器的构造方法

(4)带集合参数的构造方法

(5)带SortMap的构造方法

4、常用方法

5、TreeSet的两种排序方式

(1)实现Comparator接口,重写compare方法

 (2)实现Comparable接口,覆写compareTo()方法

三、总结

1、TreeSet总结

2、 HashSet、LinkedHashSet 以及 TreeSet。

(1)HashSet

(2)LinkedHashSet

(3)TreeSet

(4)使用场景

(5)对象的加入过程


一、TreeSet介绍

        TreeSet是一个有序的集合,基于TreeMap实现,支持两种排序方式:自然排序和定制排序。
TreeSet是非同步的,线程不安全的。

二、源码分析

1、TreeSet实现的接口

        如下图:

观察上图:

  • AbstractSet类:该类提供了Set接口的骨架实现,通过扩展此类来实现集合的过程与通过扩展AbstractCollection实现集合的过程相同,除了此类的子类中的所有方法和构造函数都必须遵守由Set接口施加的附加约束(例如,添加方法不能允许将一个对象的多个实例添加到集合中)。
  • Navigable接口:支持一系列的导航方法。比如查找与指定目标最匹配项。
  • Serializable接口:主要用于序列化,即:能够将对象写入磁盘。与之对应的还有反序列化操作,就是将对象从磁盘中读取出来。因此如果要进行序列化和反序列化,ArrayList的实例对象就必须实现这个接口,否则在实例化的时候程序会报错(java.io.NotSerializableException)。
  • Cloneable接口:实现Cloneable接口的类能够调用clone方法,如果没有实现Cloneable接口就调用方法,就会抛出异常(java.lang.CloneNotSupportedException)。

2、TreeSet中的变量

  • private transient NavigableMap<E,Object> m:存放元素的集合
  • private static final Object PRESENT = new Object():常量,构造一个虚拟的对象PRESENT,默认为map的value值(HashSet中只需要用到键,而HashMap是key-value键值对,使用PRESENT作为value的默认填充值,解决差异问题)

3、TreeSet的构造方法

(1)同包下的构造方法

  1. TreeSet(NavigableMap<E,Object> m) {
  2. this.m = m;
  3. }

(2)无参构造方法

  1. public TreeSet() {
  2. this(new TreeMap<E,Object>());
  3. }

(3)带比较器的构造方法

  1. public TreeSet(Comparator<? super E> comparator) {
  2. this(new TreeMap<>(comparator));
  3. }

(4)带集合参数的构造方法

  1. public TreeSet(Collection<? extends E> c) {
  2. this();
  3. addAll(c);
  4. }

(5)带SortMap的构造方法

  1. public TreeSet(SortedSet<E> s) {
  2. this(s.comparator());
  3. addAll(s);
  4. }

        通过上面的构造方法,可以看出TreeSet的底层是用TreeMap实现的。在构造方法中会创建一个TreeMap实例,用于存放元素,另外TreeSet是有序的,也提供了制定比较器的构造函数,如果没有提供比较器,则采用key的自然顺序进行比较大小,如果指定的比较器,则采用指定的比较器,进行key值大小的比较。

4、常用方法

  1. //遍历方法,返回m.keyset集合
  2. public Iterator<E> iterator() {
  3. return m.navigableKeySet().iterator();
  4. }
  5. //逆序排序的迭代器
  6. public Iterator<E> descendingIterator() {
  7. return m.descendingKeySet().iterator();
  8. }
  9. /**
  10. * @since 1.6
  11. */
  12. public NavigableSet<E> descendingSet() {
  13. return new TreeSet<>(m.descendingMap());
  14. }
  15. //返回 m 包含的键值对的数量
  16. public int size() {
  17. return m.size();
  18. }
  19. //是否为空
  20. public boolean isEmpty() {
  21. return m.isEmpty();
  22. }
  23. //是否包含指定的key
  24. public boolean contains(Object o) {
  25. return m.containsKey(o);
  26. }
  27. //添加元素,调用m.put方法实现
  28. public boolean add(E e) {
  29. return m.put(e, PRESENT)==null;
  30. }
  31. //删除方法,调用m.remove()方法实现
  32. public boolean remove(Object o) {
  33. return m.remove(o)==PRESENT;
  34. }
  35. //清除集合
  36. public void clear() {
  37. m.clear();
  38. }
  39. //将一个集合中的所有元素添加到TreeSet中
  40. public boolean addAll(Collection<? extends E> c) {
  41. // Use linear-time version if applicable
  42. if (m.size()==0 && c.size() > 0 &&
  43. c instanceof SortedSet &&
  44. m instanceof TreeMap) {
  45. SortedSet<? extends E> set = (SortedSet<? extends E>) c;
  46. TreeMap<E,Object> map = (TreeMap<E, Object>) m;
  47. Comparator<? super E> cc = (Comparator<? super E>) set.comparator();
  48. Comparator<? super E> mc = map.comparator();
  49. if (cc==mc || (cc != null && cc.equals(mc))) {
  50. map.addAllForTreeSet(set, PRESENT);
  51. return true;
  52. }
  53. }
  54. return super.addAll(c);
  55. }
  56. //返回子集合,通过 m.subMap()方法实现
  57. public NavigableSet<E> subSet(E fromElement, boolean fromInclusive,
  58. E toElement, boolean toInclusive) {
  59. return new TreeSet<>(m.subMap(fromElement, fromInclusive,
  60. toElement, toInclusive));
  61. }
  62. //返回set的头部
  63. public NavigableSet<E> headSet(E toElement, boolean inclusive) {
  64. return new TreeSet<>(m.headMap(toElement, inclusive));
  65. }
  66. //返回尾部
  67. public NavigableSet<E> tailSet(E fromElement, boolean inclusive) {
  68. return new TreeSet<>(m.tailMap(fromElement, inclusive));
  69. }
  70. //返回子Set
  71. public SortedSet<E> subSet(E fromElement, E toElement) {
  72. return subSet(fromElement, true, toElement, false);
  73. }
  74. //返回set的头部
  75. public SortedSet<E> headSet(E toElement) {
  76. return headSet(toElement, false);
  77. }
  78. //返回set的尾部
  79. public SortedSet<E> tailSet(E fromElement) {
  80. return tailSet(fromElement, true);
  81. }
  82. //返回m使用的比较器
  83. public Comparator<? super E> comparator() {
  84. return m.comparator();
  85. }
  86. //返回第一个元素
  87. public E first() {
  88. return m.firstKey();
  89. }
  90. //返回最后一个元素
  91. public E last() {
  92. return m.lastKey();
  93. }
  94. //返回set中小于e的最大的元素
  95. public E lower(E e) {
  96. return m.lowerKey(e);
  97. }
  98. //返回set中小于/等于e的最大元素
  99. public E floor(E e) {
  100. return m.floorKey(e);
  101. }
  102. //返回set中大于/等于e的最大元素
  103. public E ceiling(E e) {
  104. return m.ceilingKey(e);
  105. }
  106. //返回set中大于e的最小元素
  107. public E higher(E e) {
  108. return m.higherKey(e);
  109. }
  110. //获取TreeSet中第一个元素,并从Set中删除该元素
  111. public E pollFirst() {
  112. Map.Entry<E,?> e = m.pollFirstEntry();
  113. return (e == null) ? null : e.getKey();
  114. }
  115. //获取TreeSet中最后一个元素,并从Set中删除该元素
  116. public E pollLast() {
  117. Map.Entry<E,?> e = m.pollLastEntry();
  118. return (e == null) ? null : e.getKey();
  119. }
  120. //克隆方法
  121. public Object clone() {
  122. TreeSet<E> clone = null;
  123. try {
  124. clone = (TreeSet<E>) super.clone();
  125. } catch (CloneNotSupportedException e) {
  126. throw new InternalError();
  127. }
  128. clone.m = new TreeMap<>(m);
  129. return clone;
  130. }
  131. //将对象写入到输出流中。
  132. private void writeObject(java.io.ObjectOutputStream s)
  133. throws java.io.IOException {
  134. // Write out any hidden stuff
  135. s.defaultWriteObject();
  136. // Write out Comparator
  137. s.writeObject(m.comparator());
  138. // Write out size
  139. s.writeInt(m.size());
  140. // Write out all elements in the proper order.
  141. for (E e : m.keySet())
  142. s.writeObject(e);
  143. }
  144. //从输入流中读取对象的信息
  145. private void readObject(java.io.ObjectInputStream s)
  146. throws java.io.IOException, ClassNotFoundException {
  147. // Read in any hidden stuff
  148. s.defaultReadObject();
  149. // Read in Comparator
  150. Comparator<? super E> c = (Comparator<? super E>) s.readObject();
  151. // Create backing TreeMap
  152. TreeMap<E,Object> tm;
  153. if (c==null)
  154. tm = new TreeMap<>();
  155. else
  156. tm = new TreeMap<>(c);
  157. m = tm;
  158. // Read in size
  159. int size = s.readInt();
  160. tm.readTreeSet(size, s, PRESENT);
  161. }

5、TreeSet的两种排序方式

(1)实现Comparator接口,重写compare方法

  1. import java.util.*;
  2. class Student{
  3. private int id;
  4. private String name;
  5. private int age;
  6. public Student(int id, String name, int age) {
  7. this.id = id;
  8. this.name = name;
  9. this.age = age;
  10. }
  11. public int getId() {
  12. return id;
  13. }
  14. public void setId(int id) {
  15. this.id = id;
  16. }
  17. public String getName() {
  18. return name;
  19. }
  20. public void setName(String name) {
  21. this.name = name;
  22. }
  23. public int getAge() {
  24. return age;
  25. }
  26. public void setAge(int age) {
  27. this.age = age;
  28. }
  29. @Override
  30. public String toString() {
  31. return "Student{" +
  32. "id=" + id +
  33. ", name='" + name + '\'' +
  34. ", age=" + age +
  35. '}';
  36. }
  37. }
  38. class MyComparator implements Comparator{
  39. @Override
  40. public int compare(Object o1, Object o2) {
  41. Student s1 = (Student) o1;
  42. Student s2 = (Student) o2;
  43. return s1.getAge() - s2.getAge();
  44. }
  45. }
  46. public class Main {
  47. public static void main(String[] args) {
  48. TreeSet treeSet = new TreeSet(new MyComparator());
  49. treeSet.add(new Student(1, "tom", 23));
  50. treeSet.add(new Student(2, "Jerry", 20));
  51. treeSet.add(new Student(3, "Jack", 17));
  52. treeSet.add(new Student(4, "Marry", 54));
  53. treeSet.add(new Student(5, "Baby", 8));
  54. Iterator iterator = treeSet.iterator();
  55. while(iterator.hasNext()){
  56. System.out.println(iterator.next());
  57. }
  58. }
  59. }

 

 

 (2)实现Comparable接口,覆写compareTo()方法

  1. import java.util.*;
  2. class Student implements Comparable{
  3. private int id;
  4. private String name;
  5. private int age;
  6. public Student(int id, String name, int age) {
  7. this.id = id;
  8. this.name = name;
  9. this.age = age;
  10. }
  11. public int getId() {
  12. return id;
  13. }
  14. public void setId(int id) {
  15. this.id = id;
  16. }
  17. public String getName() {
  18. return name;
  19. }
  20. public void setName(String name) {
  21. this.name = name;
  22. }
  23. public int getAge() {
  24. return age;
  25. }
  26. public void setAge(int age) {
  27. this.age = age;
  28. }
  29. @Override
  30. public String toString() {
  31. return "Student{" +
  32. "id=" + id +
  33. ", name='" + name + '\'' +
  34. ", age=" + age +
  35. '}';
  36. }
  37. @Override
  38. public int compareTo(Object o) {
  39. if(!(o instanceof Student)){
  40. throw new RuntimeException("对象有问题");
  41. }
  42. Student s = (Student) o;
  43. if(this.age > s.age){
  44. return -1;
  45. }else{
  46. return 1;
  47. }
  48. }
  49. }
  50. public class Main {
  51. public static void main(String[] args) {
  52. TreeSet treeSet = new TreeSet();
  53. treeSet.add(new Student(1, "tom", 23));
  54. treeSet.add(new Student(2, "Jerry", 20));
  55. treeSet.add(new Student(3, "Jack", 17));
  56. treeSet.add(new Student(4, "Marry", 54));
  57. treeSet.add(new Student(5, "Baby", 8));
  58. Iterator iterator = treeSet.iterator();
  59. while(iterator.hasNext()){
  60. System.out.println(iterator.next());
  61. }
  62. }
  63. }

 

 

三、总结

1、TreeSet总结

  • TreeSet实际上是TreeMap实现的,所以底层结构也是红黑树。TreeSet不需要重写hashCode()和euqals()方法,因为它去重是依靠比较器来去重,因为结构是红黑树,所以每次插入都会遍历比较来寻找节点插入位置,如果发现某个节点的值是一样的那就会直接覆盖。
  • 当我们构造TreeSet时;若使用不带参数的构造函数,则TreeSet的使用自然比较器;若用户需要使用自定义的比较器,则需要使用带比较器的参数。
  • TreeSet是非线程安全的。
  • TreeSet实现java.io.Serializable的方式。当写入到输出流时,依次写入“比较器、容量、全部元素”;当读出输入流时,再依次读取。 

2、 HashSet、LinkedHashSet 以及 TreeSet。

(1)HashSet

  • 不能保证元素的排列顺序,顺序有可能发生变化
  • 不是同步的,非线程安全
  • 集合元素可以是null,但只能放入一个null
  • 当向HashSet结合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据 hashCode值来决定该对象在HashSet中存储位置。
  • 简单的说,HashSet集合判断两个元素相等的标准是两个对象通过equals方法比较相等,并且两个对象的hashCode()方法返回值相等
  • 注意,如果要把一个对象放入HashSet中,重写该对象对应类的equals方法,也应该重写其hashCode()方法。其规则是如果两个对象通过equals方法比较返回true时,其hashCode也应该相同。另外,对象中用作equals比较标准的属性,都应该用来计算hashCode的值。

(2)LinkedHashSet

        LinkedHashSet集合同样是根据元素的hashCode值来决定元素的存储位置,但是它同时使用链表维护元素的次序。这样使得元素看起 来像是以插入顺序保存的,也就是说,当遍历该集合时候,LinkedHashSet将会以元素的添加顺序访问集合的元素。

  • LinkedHashSet中不能有相同元素,可以有一个Null元素,元素严格按照放入的顺序排列。
  • LinkedHashSet底层数据结构由哈希表和链表组成,链表保证了元素的有序即存储和取出一致,哈希表保证了元素的唯一性。
  • LinkedHashSet添加、删除操作时间复杂度都是O(1)。

(3)TreeSet

  • TreeSet支持两种排序方式,自然排序 和定制排序,其中自然排序为默认的排序方式。向TreeSet中加入的应该是同一个类的对象。
  • TreeSet判断两个对象不相等的方式是两个对象通过equals方法返回false,或者通过CompareTo方法比较没有返回0
  • TreeSet是中不能有相同元素,不可以有Null元素,根据元素的自然顺序进行排序。
  • TreeSet底层的数据结构是红黑树(一种自平衡二叉查找树)
  • 添加、删除操作时间复杂度都是O(log(n))

(4)使用场景

         HashSet是基于Hash算法实现的,其性能通常都优于TreeSet。我们通常都应该使用HashSet,在我们需要排序的功能时,我们才使用TreeSet。 

(5)对象的加入过程

        TreeSet集合对象的加入过程: 
        TreeSet的底层是通过二叉树来完成存储的,无序的集合 
        当我们将一个对象加入treeset中,treeset会将第一个对象作为根对象,然后调用对象的compareTo方法拿第二个对象和第一个比较,当返回值等于0时,说明2个对象内容相等,treeset就不把第二个对象加入集合。返回>1时,说明第二个对象大于第一个对象,将第二个对象放在右边,返回-1时,则将第二个对象放在左边,依次类推 。

        HashSet集合对象的加入过程: 
        hashset底层是hash值的地址,它里面存的对象是无序的。 
        第一个对象进入集合时,hashset会调用object类的hashcode根据对象在堆内存里的地址调用对象重写的hashcode计算出一个hash值,然后第一个对象就进入hashset集合中的任意一个位置。 
        第二个对象开始进入集合,hashset先根据第二个对象在堆内存的地址调用对象的计算出一个hash值,如果第二个对象和第一个对象在堆内存里的地址是相同的,那么得到的hash值也是相同的,直接返回true,hash得到true后就不把第二个元素加入集合(这段是hash源码程序中的操作)。如果第二个对象和第一个对象在堆内存里地址是不同的,这时hashset类会先调用自己的方法遍历集合中的元素,当遍历到某个元素时,调用对象的equals方法,如果相等,返回true,则说明这两个对象的内容是相同的,hashset得到true后不会把第二个对象加入集合。 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Guff_9hys/article/detail/766104
推荐阅读
相关标签
  

闽ICP备14008679号