TreeSet与HashSet类似,不过TreeSet是一个有序集合(sorted collection),在对集合进行遍历时,每个值将自动的按照TreeSet内部排序的顺序呈现。当前TreeSet实现使用的是红黑树(red-black tree,详细介绍可以参照《算法导论》一书),每次将一个元素添加到树中时,都被放置在正确的排序位置上,因此,迭代器总是以排好序的顺序访问每个元素。
将一个元素添加到树中比添加到散列表中慢,但是比添加到数组或者链表中正确位置上要快很多。例如如果树中包含n个元素,查找新元素的正确位置平均需要log2n次比较,也就是如果树中包含1000个元素,添加一个新元素大约需要比较10次即可。
对象的比较
TreeSet如何知道元素怎么排列呢?树集假定插入的元素实现了Comparable接口,这个接口有一个定义的方法:
public interface Comparable<T>
{
int compareTo(T other);
}
有些标准的Java平台类实现类Comparable接口,例如String类,这个类的compareTo方法依据字典序(有时称为词典序)对字符串进行比较。如果要插入自定义的对象,就必须通过实现Comparable接口自定义排列顺序。在Object类中,没有提供compareTo接口的默认实现。
例如下面的代码展示了如何用部件编号对Item对象进行排序:
class Item implements Compareble<Item>
{
public int compareTo(Item other)
{
return partNumber - other.partNumber; //需要确保结果不会溢出
}
......
}
对于一个给定的类,只能实现这个接口一次,如果在一个集合中需要按照部件编号进行排序,在另一个集合中却要按照描述信息进行排序,该怎么办呢?另外,如果需要对一个类的对象进行排序,而这个类的创建者有没有实现Comparable接口,又该怎么办呢?以上两种情况下,可以通过将Comparator对象传递给TreeSet构造器来告诉树集使用不同的比较方法,Comparator接口声明了一个带有两个显式参数的compare方法。
public interface Comparator<T>
{
int compare(T a,T b);
}
如果按照描述信息进行排序,就直接定义一个实现Comparator接口的类:
class ItemComparator implements Comparator<Item>
{
public int compare(Item a, Item b)
{
String descrA = a.getDescription();
String descrB = b.getDescription();
return descrA.compareTo(descrB);
}
}
然后将这个类的对象传递给树集的构造器:
ItemComparator comp = new ItemComparator();
SortedSet<Item> sortByDescription = new TreeSet<Item>(comp);
如果构造了一棵带有比较器的树,就可以在需要比较两个元素时使用这个对象。
注意,这个比较器没有任何数据,它只是比较方法的持有器。有时将这种对象称为函数对象(function object)。函数对象通常被定义为“瞬时的”,即匿名内部类的实例:
SortedSet<Item> sortByDescription = new TreeSet<Item>(new
Comparator<Item>()
{
public int compare(Item a, Item b)
{
String descrA = a.getDescription();
String descrB = b.getDescription();
return descrA.compareTo(descrB);
}
});
注:从Java SE6开始,TreeSet类实现了NavigableSet接口,这个接口增加了几个便于定位元素以及反向遍历的方法,具体可参见API注释。
下面例子中创建了两个Item对象的树集,第一个按照编号排序,是对象的默认顺序,第二个通过使用一个定制的比较器来按照描述信息排序。代码如下:
package com.chensl.collection;
import java.util.*;
/**
This program sorts a set of item by comparing
their descriptions.
*/
public class TreeSetTest
{
public static void main(String[] args)
{
SortedSet<Item> parts = new TreeSet<Item>();
parts.add(new Item("Toaster", 1234));
parts.add(new Item("Widget", 4562));
parts.add(new Item("Modem", 9912));
System.out.println(parts);
SortedSet<Item> sortByDescription = new TreeSet<Item>(new
Comparator<Item>()
{
public int compare(Item a, Item b)
{
String descrA = a.getDescription();
String descrB = b.getDescription();
return descrA.compareTo(descrB);
}
});
sortByDescription.addAll(parts);
System.out.println(sortByDescription);
}
}
/**
An item with a description and a part number.
*/
class Item implements Comparable<Item>
{
/**
Constructs an item.
@param aDescription the item's description
@param aPartNumber the item's part number
*/
public Item(String aDescription, int aPartNumber)
{
description = aDescription;
partNumber = aPartNumber;
}
/**
Gets the description of this item.
@return the description
*/
public String getDescription()
{
return description;
}
public String toString()
{
return "[descripion=" + description
+ ", partNumber=" + partNumber + "]";
}
public boolean equals(Object otherObject)
{
if (this == otherObject) return true;
if (otherObject == null) return false;
if (getClass() != otherObject.getClass()) return false;
Item other = (Item) otherObject;
return description.equals(other.description)
&& partNumber == other.partNumber;
}
public int hashCode()
{
return 13 * description.hashCode() + 17 * partNumber;
}
public int compareTo(Item other)
{
return partNumber - other.partNumber;
}
private String description;
private int partNumber;
}