1 使用上看
- TreeSet要求,每一個元素要實現Comparable接口。
- TreeMap要求鍵實現Comparable接口。
- Collections.sort()有兩種重載方式:
(1)元素實現Comparable接口。
(2)向sort()方法中傳入一個Comparator實現類。
-
TreeSet
TreeSet要求存放的對象所屬的類必須實現Comparable接口,該接口提供了比較元素的compareTo()方法,當插入元素時會回調該方法比較元素的大小。 -
TreeMap
TreeMap要求存放的鍵值對映射的鍵必須實現Comparable接口,從而根據鍵對元素進行排序。 -
Collections.sort()
工具類的sort方法有兩種重載的形式,第一種要求傳入的待排序容器中存放的對象必須實現Comparable接口以實現元素的比較;第二種不強制性的要求容器中的元素必須可比較,但是要求傳入第二個參數,參數是Comparator接口的子類型(需要重寫compare方法實現元素的比較),相當於一個臨時定義的排序規則,其實就是通過接口注入比較元素大小的算法,也是對回調模式的應用(Java中對函數式編程的支持)。
import sun.reflect.generics.tree.Tree;
import java.util.*;
public class JavaTest {
public static void main(String[] args) {
Student student1 = new Student("zhang2",10);
Student student2 = new Student("zhang1",20);
Student student3 = new Student("zhang3",30);
/*TreeSet要求,每一個元素要實現Comparable接口。*/
Set<Student> treeSet = new TreeSet<Student>();
treeSet.add(student1);
treeSet.add(student2);
treeSet.add(student3);
for (Object o : treeSet) {
Student stu = (Student) o;
System.out.println(stu.getName() + " : " + stu.getAge());
}
System.out.println();
/*TreeMap要求鍵實現Comparable接口*/
Map map = new TreeMap();
map.put(student1,2);
map.put(student2,3);
map.put(student3,1);
Set set = map.keySet();
for (Object o : set) {
Student stu = (Student) o;
System.out.println(map.get(stu));
}
List<Student> list = new ArrayList<Student>();
list.add(student1);
list.add(student2);
list.add(student3);
/**
* Collections.sort()有兩種重載方式:
* (1)元素實現Comparable接口。
* (2)向sort()方法中傳入一個Comparator實現類。
*/
//Student實現了Comparable接口
Collections.sort(list);
for (Student student : list) {
System.out.println(student.getName() + " : " + student.getAge());
}
List<Student> list2 = new ArrayList<Student>();
list2.add(student1);
list2.add(student2);
list2.add(student3);
Collections.sort(list2, new Comparator<Student>() {
public int compare(Student o1, Student o2) {
return o1.getAge() - o2.getAge();
}
});
for (Student student : list2) {
System.out.println(student.getName() + " : " + student.getAge());
}
}
}
class Student implements Comparable<Student>{
private String name;
private Integer age;
public Student(String name,Integer age){
this.name = name;
this.age = age;
}
public int compareTo(Student stu) {
return this.name.compareTo(stu.getName());
}
public String getName() {
return name;
}
public Integer getAge() {
return age;
}
}
2 原理
2.1 TreeSet與TreeMap
參考文獻:treeSet的底層實現
TreeMap 的實現就是紅黑樹數據結構,也就說是一棵自平衡的排序二叉樹,這樣就可以保證當需要快速檢索指定節點。
TreeSet 和 TreeMap 的關係
爲了讓大家瞭解 TreeMap 和 TreeSet 之間的關係,下面先看 TreeSet 類的部分源代碼:
public class TreeSet<E> extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, java.io.Serializable
{
// 使用 NavigableMap 的 key 來保存 Set 集合的元素
private transient NavigableMap<E,Object> m;
// 使用一個 PRESENT 作爲 Map 集合的所有 value。
private static final Object PRESENT = new Object();
// 包訪問權限的構造器,以指定的 NavigableMap 對象創建 Set 集合
TreeSet(NavigableMap<E,Object> m)
{
this.m = m;
}
public TreeSet() // ①
{
// 以自然排序方式創建一個新的 TreeMap,
// 根據該 TreeSet 創建一個 TreeSet,
// 使用該 TreeMap 的 key 來保存 Set 集合的元素
this(new TreeMap<E,Object>());
}
public TreeSet(Comparator<? super E> comparator) // ②
{
// 以定製排序方式創建一個新的 TreeMap,
// 根據該 TreeSet 創建一個 TreeSet,
// 使用該 TreeMap 的 key 來保存 Set 集合的元素
this(new TreeMap<E,Object>(comparator));
}
public TreeSet(Collection<? extends E> c)
{
// 調用①號構造器創建一個 TreeSet,底層以 TreeMap 保存集合元素
this();
// 向 TreeSet 中添加 Collection 集合 c 裏的所有元素
addAll(c);
}
public TreeSet(SortedSet<E> s)
{
// 調用②號構造器創建一個 TreeSet,底層以 TreeMap 保存集合元素
this(s.comparator());
// 向 TreeSet 中添加 SortedSet 集合 s 裏的所有元素
addAll(s);
}
//TreeSet 的其他方法都只是直接調用 TreeMap 的方法來提供實現
...
public boolean addAll(Collection<? extends E> c)
{
if (m.size() == 0 && c.size() > 0 &&
c instanceof SortedSet &&
m instanceof TreeMap)
{
// 把 c 集合強制轉換爲 SortedSet 集合
SortedSet<? extends E> set = (SortedSet<? extends E>) c;
// 把 m 集合強制轉換爲 TreeMap 集合
TreeMap<E,Object> map = (TreeMap<E, Object>) m;
Comparator<? super E> cc = (Comparator<? super E>) set.comparator();
Comparator<? super E> mc = map.comparator();
// 如果 cc 和 mc 兩個 Comparator 相等
if (cc == mc || (cc != null && cc.equals(mc)))
{
// 把 Collection 中所有元素添加成 TreeMap 集合的 key
map.addAllForTreeSet(set, PRESENT);
return true;
}
}
// 直接調用父類的 addAll() 方法來實現
return super.addAll(c);
}
...
}
從上面代碼可以看出,TreeSet 的 ① 號、② 號構造器的都是新建一個 TreeMap 作爲實際存儲 Set 元素的容器,而另外 2 個構造器則分別依賴於 ① 號和 ② 號構造器,由此可見,TreeSet 底層實際使用的存儲容器就是 TreeMap。
與 HashSet 完全類似的是,TreeSet 裏絕大部分方法都是直接調用 TreeMap 的方法來實現的,這一點讀者可以自行參閱 TreeSet 的源代碼,此處就不再給出了。
對於 TreeMap 而言,它採用一種被稱爲“紅黑樹”的排序二叉樹來保存 Map 中每個 Entry —— 每個 Entry 都被當成“紅黑樹”的一個節點對待。
2.2 Collections.sort()
Collections.sort方法底層就是調用的array.sort方法。
底層是TimSort實現的,這是jdk1.7新增的,以前是歸併排序。TimSort算法就是找到**數據中已經排好序的塊,然後按規則合併這些塊。**每一個塊是一個分區,每一個分區也叫一個run。
Timsort核心的過程
TimSort算法爲了減少對升序部分的回饋和對降序部分的性能倒退,將輸入按其升序和降序特點進行了分區。每次合併會將兩個運行合併成一個運行。合併的結果保存到棧中。合併直到消耗掉所有的運行,這時將棧上剩餘的跑合併到只剩一個跑爲止。這時這個僅剩的跑便是排好序的結果。
綜上述過程,Timsort算法的過程包括
(0)如何數組長度小於某個值,直接用二分插入排序算法
(1)找到各個塊,併入棧
(2)按規則合併運行