java集合-TreeSet
一:基本概念
TreeSet基于 TreeMap 的 NavigableSet 实现。
- 使用元素的自然顺序对元素进行排序,或者根据创建 set 时提供的 Comparator 进行排序,具体取决于使用的构造方法。
- 此实现为基本操作(add、remove 和 contains)提供受保证的 log(n) 时间开销。
- 如果要正确实现 Set 接口,则 set 维护的顺序(无论是否提供了显式比较器)必须与 equals 一致。(关于与 equals 一致 的精确定义,请参阅 Comparable 或 Comparator。)这是因为 Set 接口是按照 equals 操作定义的,但 TreeSet 实例使用它的 compareTo(或 compare)方法对所有元素进行比较,因此从 set 的观点来看,此方法认为相等的两个元素就是相等的。即使 set 的顺序与 equals 不一致,其行为也是 定义良好的;它只是违背了 Set 接口的常规协定。
- TreeSet不是同步的。如果多个线程同时访问一个 TreeSet,而其中至少一个线程修改了该 set,那么它必须 外部同步。这一般是通过对自然封装该 set 的对象执行同步操作来完成的。如果不存在这样的对象,则应该使用
Collections.synchronizedSortedSet
方法来“包装”该 set。此操作最好在创建时进行,以防止对 set 的意外非同步访问:SortedSet s = Collections.synchronizedSortedSet(new TreeSet(...));
- 此类的 iterator 方法返回的迭代器是快速失败 的:在创建迭代器之后,如果从结构上对 set 进行修改,除非通过迭代器自身的 remove 方法,否则在其他任何时间以任何方式进行修改都将导致迭代器抛出 ConcurrentModificationException。因此,对于并发的修改,迭代器很快就完全失败,而不会冒着在将来不确定的时间发生不确定行为的风险。
- 注迭代器的快速失败行为无法得到保证,一般来说,存在不同步的并发修改时,不可能作出任何肯定的保证。快速失败迭代器尽最大努力抛出 ConcurrentModificationException。
实现结构图如下:(来源互联网)
public class TreeSet<E>
extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, Serializable
API 简单测试:
package com.csu.collection;
import java.util.Iterator;
import java.util.TreeSet;
public class TreeSetTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
testTreeSetAPIs();
}
public static void testTreeSetAPIs() {
String val;
TreeSet<String> tSet = new TreeSet<>();
tSet.add("aaa");
// Set中不允许重复元素,所以只会保存一个“aaa”
tSet.add("aaa");
tSet.add("bbb");
tSet.add("eee");
tSet.add("ddd");
tSet.add("ccc");
System.out.println("TreeSet:"+tSet);
// 打印TreeSet的实际大小
System.out.printf("size : %d\n", tSet.size());
// 导航方法
// floor(小于、等于)
System.out.printf("floor bbb: %s\n", tSet.floor("bbb"));
// lower(小于)
System.out.printf("lower bbb: %s\n", tSet.lower("bbb"));
// ceiling(大于、等于)
System.out.printf("ceiling bbb: %s\n", tSet.ceiling("bbb"));
System.out.printf("ceiling eee: %s\n", tSet.ceiling("eee"));
// ceiling(大于)
System.out.printf("higher bbb: %s\n", tSet.higher("bbb"));
// subSet()
System.out.printf("subSet(aaa, true, ccc, true): %s\n", tSet.subSet("aaa", true, "ccc", true));
System.out.printf("subSet(aaa, true, ccc, false): %s\n", tSet.subSet("aaa", true, "ccc", false));
System.out.printf("subSet(aaa, false, ccc, true): %s\n", tSet.subSet("aaa", false, "ccc", true));
System.out.printf("subSet(aaa, false, ccc, false): %s\n", tSet.subSet("aaa", false, "ccc", false));
// headSet()
System.out.printf("headSet(ccc, true): %s\n", tSet.headSet("ccc", true));
System.out.printf("headSet(ccc, false): %s\n", tSet.headSet("ccc", false));
// tailSet()
System.out.printf("tailSet(ccc, true): %s\n", tSet.tailSet("ccc", true));
System.out.printf("tailSet(ccc, false): %s\n", tSet.tailSet("ccc", false));
// 删除“ccc”
tSet.remove("ccc");
// 将Set转换为数组
String[] arr = (String[])tSet.toArray(new String[0]);
for (String str:arr)
System.out.printf("for each : %s\n", str);
// 打印TreeSet
System.out.printf("TreeSet:%s\n", tSet);
// 遍历TreeSet
for(Iterator<String> iter = tSet.iterator(); iter.hasNext(); ) {
System.out.printf("iter : %s\n", iter.next());
}
// 删除并返回第一个元素
val = (String)tSet.pollFirst();
System.out.printf("pollFirst=%s, set=%s\n", val, tSet);
// 删除并返回最后一个元素
val = (String)tSet.pollLast();
System.out.printf("pollLast=%s, set=%s\n", val, tSet);
// 清空HashSet
tSet.clear();
// 输出HashSet是否为空
System.out.printf("%s\n", tSet.isEmpty()?"set is empty":"set is not empty");
}
}
运行结果:
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
二:源码分析
1:变量定义:
NavigableMap对象,后边的集合操作就是基于它来操作的。
private transient NavigableMap<E,Object> m;
2:构造函数
public TreeSet() {
this(new TreeMap<E,Object>());
}
创建一个空集,我们可以看出这个集合采用TreeMap来构造,以集合的值为Treemap的Key,因此集合不能存储重复的数据,这个构造函数使得集合排序按照TreeMap的自然顺序。
public TreeSet(Comparator<? super E> comparator) {
this(new TreeMap<>(comparator));
}
构造一个新的空 TreeSet,它根据指定比较器进行排序。也就是说,我们可以通过自己实现comparator接口来自定义排序。
public TreeSet(Collection<? extends E> c) {
this();
addAll(c);
}
构造一个包含指定 collection 元素的新 TreeSet,它按照其元素的自然顺序进行排序。我们可以看出通过调用addAll()方法将这个数据插入到集合中。
public TreeSet(SortedSet<E> s) {
this(s.comparator());
addAll(s);
}
同样的道理,构造一个包含SortedSet的集合。
3:主要方法分析:
iterator():
public Iterator<E> iterator() {
return m.navigableKeySet().iterator();
}
我们可以看出TreeSet的迭代器是通过 NavigableMap对象的中KeySet方法获取的。
contains():
public boolean contains(Object o) {
return m.containsKey(o);
}
调用的事map的containsKey()方法;
add();
public boolean add(E e) {
return m.put(e, PRESENT)==null;
}
也是直接调用map的方法。
addAll():
public boolean addAll(Collection<? extends E> c) {
// Use linear-time version if applicable
if (m.size()==0 && c.size() > 0 &&
c instanceof SortedSet &&
m instanceof TreeMap) {
SortedSet<? extends E> set = (SortedSet<? extends E>) c;
TreeMap<E,Object> map = (TreeMap<E, Object>) m;
Comparator<?> cc = set.comparator();
Comparator<? super E> mc = map.comparator();
if (cc==mc || (cc != null && cc.equals(mc))) {
//还是通过调用TreeMap的方法添加元素
map.addAllForTreeSet(set, PRESENT);
return true;
}
}
return super.addAll(c);
}
将指定 collection 中的所有元素添加到此 set 中。
应用实例
通TreeSet实现全排列
测试端:
mport java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
public class TreeSetTest1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Set<String> set=new TreeSet<>();
char []arr={'a','c','c','d'};
permutations(set, arr, 0, arr.length);
Iterator<String> iterator=set.iterator();
while(iterator.hasNext())
{
System.out.println(iterator.next());
}
}
public static void swap(char[] arr,int x,int y)
{
char temp=arr[x];
arr[x]=arr[y];
arr[y]=temp;
}
public static void permutations(Set<String> set,char[] arr,int start,int end)
{
if(start==end-1)
{
set.add(new String(arr));
}
for(int i=start;i<end;i++)
{
swap(arr, start, i);
permutations(set, arr, start+1, end);
swap(arr, start, i);
}
}
}
运行结果:
accd
acdc
adcc
cacd
cadc
ccad
ccda
cdac
cdca
dacc
dcac
dcca
天道酬勤,厚德载物!