Java 集合系列 17 之 TreeSet 详细介绍 (源码解析) 和使用示例

Java 集合系列 17 之 TreeSet 详细介绍 (源码解析) 和使用示例

概要

这一章,我们对 TreeSet 进行学习。
我们先对 TreeSet 有个整体认识,然后再学习它的源码,最后再通过实例来学会使用 TreeSet。内容包括:
第 1 部分 TreeSet 介绍
第 2 部分 TreeSet 数据结构
第 3 部分 TreeSet 源码解析 (基于 JDK1.6.0_45)
第 4 部分 TreeSet 遍历方式
第 5 部分 TreeSet 示例

转载请注明出处:http://www.cnblogs.com/skywang12345/admin/EditPosts.aspx?postid=3311268

第 1 部分 TreeSet 介绍

TreeSet 简介

TreeSet 是一个有序的集合,它的作用是提供有序的 Set 集合。它继承于 AbstractSet 抽象类,实现了 NavigableSet, Cloneable, java.io.Serializable 接口。
TreeSet 继承于 AbstractSet,所以它是一个 Set 集合,具有 Set 的属性和方法。
TreeSet 实现了 NavigableSet 接口,意味着它支持一系列的导航方法。比如查找与指定目标最匹配项。
TreeSet 实现了 Cloneable 接口,意味着它能被克隆。
TreeSet 实现了 java.io.Serializable 接口,意味着它支持序列化。

TreeSet 是基于 TreeMap 实现的。TreeSet 中的元素支持 2 种排序方式:自然排序 或者 根据创建 TreeSet 时提供的 Comparator 进行排序。这取决于使用的构造方法。
TreeSet 为基本操作(add、remove 和 contains)提供受保证的 log(n) 时间开销。
另外,TreeSet 是非同步的。 它的 iterator 方法返回的迭代器是 fail-fast 的。

TreeSet 的构造函数

[img](javascript:void(0)😉

// 默认构造函数。使用该构造函数,TreeSet中的元素按照自然排序进行排列。
TreeSet()

// 创建的TreeSet包含collection
TreeSet(Collection<? extends E> collection)

// 指定TreeSet的比较器
TreeSet(Comparator<? super E> comparator)

// 创建的TreeSet包含set
TreeSet(SortedSet<E> set)

[img](javascript:void(0)😉

TreeSet 的 API

[img](javascript:void(0)😉

boolean                   add(E object)
boolean                   addAll(Collection<? extends E> collection)
void                      clear()
Object                    clone()
boolean                   contains(Object object)
E                         first()
boolean                   isEmpty()
E                         last()
E                         pollFirst()
E                         pollLast()
E                         lower(E e)
E                         floor(E e)
E                         ceiling(E e)
E                         higher(E e)
boolean                   remove(Object object)
int                       size()
Comparator<? super E>     comparator()
Iterator<E>               iterator()
Iterator<E>               descendingIterator()
SortedSet<E>              headSet(E end)
NavigableSet<E>           descendingSet()
NavigableSet<E>           headSet(E end, boolean endInclusive)
SortedSet<E>              subSet(E start, E end)
NavigableSet<E>           subSet(E start, boolean startInclusive, E end, boolean endInclusive)
NavigableSet<E>           tailSet(E start, boolean startInclusive)
SortedSet<E>              tailSet(E start)

[img](javascript:void(0)😉

说明

(01) TreeSet 是有序的 Set 集合,因此支持 add、remove、get 等方法。
(02) 和 NavigableSet 一样,TreeSet 的导航方法大致可以区分为两类,一类时提供元素项的导航方法,返回某个元素;另一类时提供集合的导航方法,返回某个集合。
lower、floor、ceiling 和 higher 分别返回小于、小于等于、大于等于、大于给定元素的元素,如果不存在这样的元素,则返回 null。

第 2 部分 TreeSet 数据结构

TreeSet 的继承关系

[img](javascript:void(0)😉

java.lang.Object
   ↳     java.util.AbstractCollection<E>
         ↳     java.util.AbstractSet<E>
               ↳     java.util.TreeSet<E>

public class TreeSet<E> extends AbstractSet<E>        
    implements NavigableSet<E>, Cloneable, java.io.Serializable{}

[img](javascript:void(0)😉

TreeSet 与 Collection 关系如下图:

img

从图中可以看出:
(01) TreeSet 继承于 AbstractSet,并且实现了 NavigableSet 接口。
(02) TreeSet 的本质是一个 "有序的,并且没有重复元素" 的集合,它是通过 TreeMap 实现的。TreeSet 中含有一个 "NavigableMap 类型的成员变量"m,而 m 实际上是 "TreeMap 的实例"。

第 3 部分 TreeSet 源码解析 (基于 JDK1.6.0_45)

为了更了解 TreeSet 的原理,下面对 TreeSet 源码代码作出分析。

img

img

[img](javascript:void(0)😉

1 package java.util;
  3 public class TreeSet<E> extends AbstractSet<E>
  4     implements NavigableSet<E>, Cloneable, java.io.Serializable
  5 {
  6     // NavigableMap对象
  7     private transient NavigableMap<E,Object> m;
  9     // TreeSet是通过TreeMap实现的,
 10     // PRESENT是键-值对中的值。
 11     private static final Object PRESENT = new Object();
 13     // 不带参数的构造函数。创建一个空的TreeMap
 14     public TreeSet() {
 15         this(new TreeMap<E,Object>());
 16     }
 18     // 将TreeMap赋值给 "NavigableMap对象m"
 19     TreeSet(NavigableMap<E,Object> m) {
 20         this.m = m;
 21     }
 23     // 带比较器的构造函数。
 24     public TreeSet(Comparator<? super E> comparator) {
 25         this(new TreeMap<E,Object>(comparator));
 26     }
 28     // 创建TreeSet,并将集合c中的全部元素都添加到TreeSet中
 29     public TreeSet(Collection<? extends E> c) {
 30         this();
 31         // 将集合c中的元素全部添加到TreeSet中
 32         addAll(c);
 33     }
 35     // 创建TreeSet,并将s中的全部元素都添加到TreeSet中
 36     public TreeSet(SortedSet<E> s) {
 37         this(s.comparator());
 38         addAll(s);
 39     }
 41     // 返回TreeSet的顺序排列的迭代器。
 42     // 因为TreeSet时TreeMap实现的,所以这里实际上时返回TreeMap的“键集”对应的迭代器
 43     public Iterator<E> iterator() {
 44         return m.navigableKeySet().iterator();
 45     }
 47     // 返回TreeSet的逆序排列的迭代器。
 48     // 因为TreeSet时TreeMap实现的,所以这里实际上时返回TreeMap的“键集”对应的迭代器
 49     public Iterator<E> descendingIterator() {
 50         return m.descendingKeySet().iterator();
 51     }
 53     // 返回TreeSet的大小
 54     public int size() {
 55         return m.size();
 56     }
 58     // 返回TreeSet是否为空
 59     public boolean isEmpty() {
 60         return m.isEmpty();
 61     }
 63     // 返回TreeSet是否包含对象(o)
 64     public boolean contains(Object o) {
 65         return m.containsKey(o);
 66     }
 68     // 添加e到TreeSet中
 69     public boolean add(E e) {
 70         return m.put(e, PRESENT)==null;
 71     }
 73     // 删除TreeSet中的对象o
 74     public boolean remove(Object o) {
 75         return m.remove(o)==PRESENT;
 76     }
 78     // 清空TreeSet
 79     public void clear() {
 80         m.clear();
 81     }
 83     // 将集合c中的全部元素添加到TreeSet中
 84     public  boolean addAll(Collection<? extends E> c) {
 85         // Use linear-time version if applicable
 86         if (m.size()==0 && c.size() > 0 &&
 87             c instanceof SortedSet &&
 88             m instanceof TreeMap) {
 89             SortedSet<? extends E> set = (SortedSet<? extends E>) c;
 90             TreeMap<E,Object> map = (TreeMap<E, Object>) m;
 91             Comparator<? super E> cc = (Comparator<? super E>) set.comparator();
 92             Comparator<? super E> mc = map.comparator();
 93             if (cc==mc || (cc != null && cc.equals(mc))) {
 94                 map.addAllForTreeSet(set, PRESENT);
 95                 return true;
 96             }
 97         }
 98         return super.addAll(c);
 99     }
101     // 返回子Set,实际上是通过TreeMap的subMap()实现的。
102     public NavigableSet<E> subSet(E fromElement, boolean fromInclusive,
103                                   E toElement,   boolean toInclusive) {
104         return new TreeSet<E>(m.subMap(fromElement, fromInclusive,
105                                        toElement,   toInclusive));
106     }
108     // 返回Set的头部,范围是:从头部到toElement。
109     // inclusive是是否包含toElement的标志
110     public NavigableSet<E> headSet(E toElement, boolean inclusive) {
111         return new TreeSet<E>(m.headMap(toElement, inclusive));
112     }
114     // 返回Set的尾部,范围是:从fromElement到结尾。
115     // inclusive是是否包含fromElement的标志
116     public NavigableSet<E> tailSet(E fromElement, boolean inclusive) {
117         return new TreeSet<E>(m.tailMap(fromElement, inclusive));
118     }
120     // 返回子Set。范围是:从fromElement(包括)到toElement(不包括)。
121     public SortedSet<E> subSet(E fromElement, E toElement) {
122         return subSet(fromElement, true, toElement, false);
123     }
125     // 返回Set的头部,范围是:从头部到toElement(不包括)。
126     public SortedSet<E> headSet(E toElement) {
127         return headSet(toElement, false);
128     }
130     // 返回Set的尾部,范围是:从fromElement到结尾(不包括)。
131     public SortedSet<E> tailSet(E fromElement) {
132         return tailSet(fromElement, true);
133     }
135     // 返回Set的比较器
136     public Comparator<? super E> comparator() {
137         return m.comparator();
138     }
140     // 返回Set的第一个元素
141     public E first() {
142         return m.firstKey();
143     }
145     // 返回Set的最后一个元素
146     public E first() {
147     public E last() {
148         return m.lastKey();
149     }
151     // 返回Set中小于e的最大元素
152     public E lower(E e) {
153         return m.lowerKey(e);
154     }
156     // 返回Set中小于/等于e的最大元素
157     public E floor(E e) {
158         return m.floorKey(e);
159     }
161     // 返回Set中大于/等于e的最小元素
162     public E ceiling(E e) {
163         return m.ceilingKey(e);
164     }
166     // 返回Set中大于e的最小元素
167     public E higher(E e) {
168         return m.higherKey(e);
169     }
171     // 获取第一个元素,并将该元素从TreeMap中删除。
172     public E pollFirst() {
173         Map.Entry<E,?> e = m.pollFirstEntry();
174         return (e == null)? null : e.getKey();
175     }
177     // 获取最后一个元素,并将该元素从TreeMap中删除。
178     public E pollLast() {
179         Map.Entry<E,?> e = m.pollLastEntry();
180         return (e == null)? null : e.getKey();
181     }
183     // 克隆一个TreeSet,并返回Object对象
184     public Object clone() {
185         TreeSet<E> clone = null;
186         try {
187             clone = (TreeSet<E>) super.clone();
188         } catch (CloneNotSupportedException e) {
189             throw new InternalError();
190         }
192         clone.m = new TreeMap<E,Object>(m);
193         return clone;
194     }
196     // java.io.Serializable的写入函数
197     // 将TreeSet的“比较器、容量,所有的元素值”都写入到输出流中
198     private void writeObject(java.io.ObjectOutputStream s)
199         throws java.io.IOException {
200         s.defaultWriteObject();
202         // 写入比较器
203         s.writeObject(m.comparator());
205         // 写入容量
206         s.writeInt(m.size());
208         // 写入“TreeSet中的每一个元素”
209         for (Iterator i=m.keySet().iterator(); i.hasNext(); )
210             s.writeObject(i.next());
211     }
213     // java.io.Serializable的读取函数:根据写入方式读出
214     // 先将TreeSet的“比较器、容量、所有的元素值”依次读出
215     private void readObject(java.io.ObjectInputStream s)
216         throws java.io.IOException, ClassNotFoundException {
217         // Read in any hidden stuff
218         s.defaultReadObject();
220         // 从输入流中读取TreeSet的“比较器”
221         Comparator<? super E> c = (Comparator<? super E>) s.readObject();
223         TreeMap<E,Object> tm;
224         if (c==null)
225             tm = new TreeMap<E,Object>();
226         else
227             tm = new TreeMap<E,Object>(c);
228         m = tm;
230         // 从输入流中读取TreeSet的“容量”
231         int size = s.readInt();
233         // 从输入流中读取TreeSet的“全部元素”
234         tm.readTreeSet(size, s, PRESENT);
235     }
237     // TreeSet的序列版本号
238     private static final long serialVersionUID = -2479143000061671589L;
239 }

注意看add方法:

 68     // 添加e到TreeSet中
 69     public boolean add(E e) {
 70         return m.put(e, PRESENT)==null;
 71     }

key就是我们要保存的值,value是一个PRESENT,这是什么?

 10     // PRESENT是键-值对中的值。
 11     private static final Object PRESENT = new Object();

可以看到,PRESENT就是TreeSet创建时创建的一个Object对象,它的唯一作用就是占位做Value

我们也可以从中看出,TreeSet真的是很依赖于TreeMap,许多方法干脆就是直接调用TreeMap中的同类方法,算是一个比较“浅”的子类吧

总结

(01) TreeSet 实际上是 TreeMap 实现的。当我们构造 TreeSet 时;若使用不带参数的构造函数,则 TreeSet 的使用自然比较器;若用户需要使用自定义的比较器,则需要使用带比较器的参数。
(02) TreeSet 是非线程安全的。
(03) TreeSet 实现 java.io.Serializable 的方式。当写入到输出流时,依次写入 “比较器、容量、全部元素”;当读出输入流时,再依次读取。

第 4 部分 TreeSet 遍历方式

4.1 Iterator 顺序遍历

for(Iterator iter = set.iterator(); iter.hasNext(); ) { 
    iter.next();
}

4.2 Iterator 顺序遍历

// 假设set是TreeSet对象
for(Iterator iter = set.descendingIterator(); iter.hasNext(); ) { 
    iter.next();
}

4.3 for-each 遍历 HashSet

// 假设set是TreeSet对象,并且set中元素是String类型
String[] arr = (String[])set.toArray(new String[0]);
for (String str:arr)
    System.out.printf("for each : %s\n", str);

**
TreeSet 不支持快速随机遍历,只能通过迭代器进行遍历!**

TreeSet 遍历测试程序如下:

img

img

1 import java.util.*;
 3 /**
 4  * @desc TreeSet的遍历程序
 5  *
 6  * @author skywang
 7  * @email kuiwu-wang@163.com
 8  */
 9 public class TreeSetIteratorTest {
11     public static void main(String[] args) {
12         TreeSet set = new TreeSet();
13         set.add("aaa");
14         set.add("aaa");
15         set.add("bbb");
16         set.add("eee");
17         set.add("ddd");
18         set.add("ccc");
20         // 顺序遍历TreeSet
21         ascIteratorThroughIterator(set) ;
22         // 逆序遍历TreeSet
23         descIteratorThroughIterator(set);
24         // 通过for-each遍历TreeSet。不推荐!此方法需要先将Set转换为数组
25         foreachTreeSet(set);
26     }
28     // 顺序遍历TreeSet
29     public static void ascIteratorThroughIterator(TreeSet set) {
30         System.out.print("\n ---- Ascend Iterator ----\n");
31         for(Iterator iter = set.iterator(); iter.hasNext(); ) {
32             System.out.printf("asc : %s\n", iter.next());
33         }
34     }
36     // 逆序遍历TreeSet
37     public static void descIteratorThroughIterator(TreeSet set) {
38         System.out.printf("\n ---- Descend Iterator ----\n");
39         for(Iterator iter = set.descendingIterator(); iter.hasNext(); )
40             System.out.printf("desc : %s\n", (String)iter.next());
41     }
43     // 通过for-each遍历TreeSet。不推荐!此方法需要先将Set转换为数组
44     private static void foreachTreeSet(TreeSet set) {
45         System.out.printf("\n ---- For-each ----\n");
46         String[] arr = (String[])set.toArray(new String[0]);
47         for (String str:arr)
48             System.out.printf("for each : %s\n", str);
49     }
50 }

View Code

运行结果

[img](javascript:void(0)😉

---- Ascend Iterator ----
asc : aaa
asc : bbb
asc : ccc
asc : ddd
asc : eee

 ---- Descend Iterator ----
desc : eee
desc : ddd
desc : ccc
desc : bbb
desc : aaa

 ---- For-each ----
for each : aaa
for each : bbb
for each : ccc
for each : ddd
for each : eee

[img](javascript:void(0)😉

第 5 部分 TreeSet 示例

下面通过实例学习如何使用 TreeSet

img

img

[img](javascript:void(0)😉

1 import java.util.*;
 3 /**
 4  * @desc TreeSet的API测试
 5  *
 6  * @author skywang
 7  * @email kuiwu-wang@163.com
 8  */
 9 public class TreeSetTest {
11     public static void main(String[] args) {
12         testTreeSetAPIs();
13     }
15     // 测试TreeSet的api
16     public static void testTreeSetAPIs() {
17         String val;
19         // 新建TreeSet
20         TreeSet tSet = new TreeSet();
21         // 将元素添加到TreeSet中
22         tSet.add("aaa");
23         // Set中不允许重复元素,所以只会保存一个“aaa”
24         tSet.add("aaa");
25         tSet.add("bbb");
26         tSet.add("eee");
27         tSet.add("ddd");
28         tSet.add("ccc");
29         System.out.println("TreeSet:"+tSet);
31         // 打印TreeSet的实际大小
32         System.out.printf("size : %d\n", tSet.size());
34         // 导航方法
35         // floor(小于、等于)
36         System.out.printf("floor bbb: %s\n", tSet.floor("bbb"));
37         // lower(小于)
38         System.out.printf("lower bbb: %s\n", tSet.lower("bbb"));
39         // ceiling(大于、等于)
40         System.out.printf("ceiling bbb: %s\n", tSet.ceiling("bbb"));
41         System.out.printf("ceiling eee: %s\n", tSet.ceiling("eee"));
42         // ceiling(大于)
43         System.out.printf("higher bbb: %s\n", tSet.higher("bbb"));
44         // subSet()
45         System.out.printf("subSet(aaa, true, ccc, true): %s\n", tSet.subSet("aaa", true, "ccc", true));
46         System.out.printf("subSet(aaa, true, ccc, false): %s\n", tSet.subSet("aaa", true, "ccc", false));
47         System.out.printf("subSet(aaa, false, ccc, true): %s\n", tSet.subSet("aaa", false, "ccc", true));
48         System.out.printf("subSet(aaa, false, ccc, false): %s\n", tSet.subSet("aaa", false, "ccc", false));
49         // headSet()
50         System.out.printf("headSet(ccc, true): %s\n", tSet.headSet("ccc", true));
51         System.out.printf("headSet(ccc, false): %s\n", tSet.headSet("ccc", false));
52         // tailSet()
53         System.out.printf("tailSet(ccc, true): %s\n", tSet.tailSet("ccc", true));
54         System.out.printf("tailSet(ccc, false): %s\n", tSet.tailSet("ccc", false));
57         // 删除“ccc”
58         tSet.remove("ccc");
59         // 将Set转换为数组
60         String[] arr = (String[])tSet.toArray(new String[0]);
61         for (String str:arr)
62             System.out.printf("for each : %s\n", str);
64         // 打印TreeSet
65         System.out.printf("TreeSet:%s\n", tSet);
67         // 遍历TreeSet
68         for(Iterator iter = tSet.iterator(); iter.hasNext(); ) {
69             System.out.printf("iter : %s\n", iter.next());
70         }
72         // 删除并返回第一个元素
73         val = (String)tSet.pollFirst();
74         System.out.printf("pollFirst=%s, set=%s\n", val, tSet);
76         // 删除并返回最后一个元素
77         val = (String)tSet.pollLast();
78         System.out.printf("pollLast=%s, set=%s\n", val, tSet);
80         // 清空HashSet
81         tSet.clear();
83         // 输出HashSet是否为空
84         System.out.printf("%s\n", tSet.isEmpty()?"set is empty":"set is not empty");
85     }
86 }

[img](javascript:void(0)😉

View Code

运行结果

[img](javascript:void(0)😉

TreeSet:[aaa, bbb, ccc, ddd, eee]
size : 5
floor bbb: bbb
lower bbb: aaa
ceiling bbb: bbb
ceiling eee: eee
higher bbb: ccc
subSet(aaa, true, ccc, true): [aaa, bbb, ccc]
subSet(aaa, true, ccc, false): [aaa, bbb]
subSet(aaa, false, ccc, true): [bbb, ccc]
subSet(aaa, false, ccc, false): [bbb]
headSet(ccc, true): [aaa, bbb, ccc]
headSet(ccc, false): [aaa, bbb]
tailSet(ccc, true): [ccc, ddd, eee]
tailSet(ccc, false): [ddd, eee]
for each : aaa
for each : bbb
for each : ddd
for each : eee
TreeSet:[aaa, bbb, ddd, eee]
iter : aaa
iter : bbb
iter : ddd
iter : eee
pollFirst=aaa, set=[bbb, ddd, eee]
pollLast=eee, set=[bbb, ddd]
set is empty

[img](javascript:void(0)😉

posted @ 2020-03-10 16:28  别再闹了  阅读(337)  评论(0)    收藏  举报