處理TreeSet類的排序問題。本站提示廣大學習愛好者:(處理TreeSet類的排序問題)文章只能為提供參考,不一定能成為您想要的結果。以下是處理TreeSet類的排序問題正文
TreeSet支持兩種排序辦法:自然排序和定制排序。TreeSet默許采用自然排序。
1、自然排序
TreeSet會調用集合元素的compareTo(Object obj)辦法來比擬元素之間大小關系,然後將集合元素按升序陳列,這種方式就是自然排序。(比擬的前提:兩個對象的類型相反)。
java提供了一個Comparable接口,該接口裡定義了一個compareTo(Object obj)辦法,該辦法前往一個整數值,完成該接口的類必需完成該辦法,完成了該接口的類的對象就可以比擬大小。當一個對象調用該辦法與另一個對象停止比擬,例如obj1.comparTo(obj2),假如該辦法前往0,則標明這兩個對象相等;假如前往一個正整數,則標明obj1大於obj2;假如該辦法前往一個負整數,則標明obj1小於obj2.
java常用類完成Comparable接口,並提供了比擬大小的規范。完成Comparable接口的常用類:
BigDecimal、BigIneger以及一切數值型對應包裝類:按它們對應的數值的大小停止比擬。 Character:按字符的UNICODE值停止比擬。 Boolean:true對應的包裝類實例大於false對應的包裝類實例。 String:按字符串中字符的UNICODE值停止比擬。 Date、Time:前面的時間、日期比後面的時間、日期大。假如試圖把一個對象添加進TreeSet時,則該對象的類必需完成Comparable接口。
如下順序則會報錯:
class Err
{
}
public class TestTreeSetError
{
public static void main(String[] args)
{
TreeSet ts = new TreeSet();
//向TreeSet集合中添加兩個Err對象
ts.add(new Err());
ts.add(new Err());
}
}
闡明:
下面順序試圖向TreeSet集合中添加2個Err對象,添加第一個對象時,TreeSet裡沒有任何元素,所以沒有問題;當添加第二個Err對象時,TreeSet就會調用該對象的compareTo(Object obj)辦法與集合中其他元素停止比擬——假如對應的類沒有完成Comparable接口,則會引發ClassCastException異常。而且當試圖從TreeSet中取出元素第一個元素時,仍然會引發ClassCastException異常。
當采用compareTo(Object obj)辦法比擬對象時,都需求將被比擬對象obj強迫類型轉換成相反類型,由於只要相反類的兩個實例才干比擬大小。即向TreeSet中添加的應該是同一個類的對象,否則會引發ClassCastException異常。例如,當向TreeSet中添加一個字符串對象,這個操作完全正常。當添加第二個Date對象時,TreeSet就好調用該對象的compareTo(Object obj)辦法與集合中其他元素停止比擬,則此時順序會引發異常。
在實踐編程中,順序員可以定義自己的類向TreeSet中添加多品種型的對象,前提是用戶自定義類完成了Comparable接口,完成該接口時在完成compareTo(Object obj)辦法時沒有停止強迫類型轉換。但當操作TreeSet裡的集合數據時,不同類型的元素仍然會發作ClassCastExceptio異常。(仔細閱讀下就會明白)
當把一個對象參加TreeSet集合中時,TreeSet調用該對象的compareTo(Object obj)辦法與容器中的其他對象比擬大小,然後依據紅黑樹算法決議它的存儲地位。假如兩個對象經過compareTo(Object obj)比擬相等,TreeSet即以為它們存儲同一地位。
關於TreeSet集合而言,它判別兩個對象不相等的規范是:兩個對象經過equals辦法比擬前往false,或經過compareTo(Object obj)比擬沒有前往0——即便兩個對象時同一個對象,TreeSet也會把它們當成兩個對象停止處置。
如下順序所示:
//Z類,重寫了equals辦法,總是前往false,
//重寫了compareTo(Object obj)辦法,總是前往正整數
class Z implements Comparable
{
int age;
public Z(int age)
{
this.age = age;
}
public boolean equals(Object obj)
{
return false;
}
public int compareTo(Object obj)
{
return 1;
}
}
public class TestTreeSet
{
public static void main(String[] args)
{
TreeSet set = new TreeSet();
Z z1 = new Z(6);
set.add(z1);
System.out.println(set.add(z1));
//上面輸入set集合,將看到有2個元素
System.out.println(set);
//修正set集合的第一個元素的age屬性
((Z)(set.first())).age = 9;
//輸入set集合的最後一個元素的age屬性,將看到也變成了9
System.out.println(((Z)(set.last())).age);
}
}
順序運轉後果:
true
[TreeSet.Z@1fb8ee3, TreeSet.Z@1fb8ee3]
9
闡明:
順序中把同一個對象添加了兩次,由於z1對象的equals()辦法總是前往false,而且compareTo(Object obj)辦法總是前往1。這樣TreeSet會以為z1對象和它自己也不相反,因而TreeSet中添加兩個z1對象。而TreeSet對象保管的兩個元素實踐上是同一個元素。所以當修正TreeSet集合裡第一個元素的age屬性後,該TreeSet集合裡最後一個元素的age屬性也隨之改動了。
總結:當需求把一個對象放入TreeSet中時,重寫該對象對應類的equals()辦法時,應保證該辦法與compareTo(Object obj)辦法有分歧後果,其規則是:假如兩個對象經過equals辦法比擬前往true時,這兩個對象經過compareTo(Object obj)辦法比擬應前往0。
假如兩個對象經過equals辦法比擬前往true,但這兩個對象經過compareTo(Object obj)辦法比擬不前往0時,這將招致TreeSet將會把這兩個對象保管在不同地位,從而兩個對象都可以添加成功,這與Set集合的規則有點出入。
假如兩個對象經過compareTo(Object obj)辦法比擬前往0時,但它們經過equals辦法比擬前往false時將更費事:由於兩個對象經過compareTo(Object obj)辦法比擬相等,TreeSet將試圖把它們保管在同一個地位,但實踐上又不行(否則將只剩下一個對象),所以處置起來比擬費事。
假如向TreeSet中添加一個可變對象後,並且前面順序修正了該可變對象的屬性,招致它與其他對象的大小順序發作改動,但TreeSet不會再次調整它們的順序,甚至能夠招致TreeSet中保管這兩個對象,它們經過equals辦法比擬前往true,compareTo(Object obj)辦法比擬前往0.
如下順序所示:
class R
{
int count;
public R(int count)
{
this.count = count;
}
public String toString()
{
return "R(count屬性:" + count + ")";
}
public boolean equals(Object obj)
{
if (obj instanceof R)
{
R r = (R)obj;
if (r.count == this.count)
{
return true;
}
}
return false;
}
public int hashCode()
{
return this.count;
}
}
public class TestHashSet2
{
public static void main(String[] args)
{
HashSet hs = new HashSet();
hs.add(new R(5));
hs.add(new R(-3));
hs.add(new R(9));
hs.add(new R(-2));
//打印TreeSet集合,集合元素是有序陳列的
System.out.println(hs);
//取出第一個元素
Iterator it = hs.iterator();
R first = (R)it.next();
//為第一個元素的count屬性賦值
first.count = -3;
//再次輸入count將看到TreeSet裡的元素處於無序形態
System.out.println(hs);
hs.remove(new R(-3));
System.out.println(hs);
//輸入false
System.out.println("hs能否包括count為-3的R對象?" + hs.contains(new R(-3)));
//輸入false
System.out.println("hs能否包括count為5的R對象?" + hs.contains(new R(5)));
}
}
順序運轉後果:
[R(count屬性:-3), R(count屬性:-2), R(count屬性:5), R(count屬性:9)]
[R(count屬性:20), R(count屬性:-2), R(count屬性:5), R(count屬性:-2)]
[R(count屬性:20), R(count屬性:-2), R(count屬性:5), R(count屬性:-2)]
[R(count屬性:20), R(count屬性:-2), R(count屬性:-2)]
闡明:
下面順序中的R對象是一個正常重寫了equals辦法和comparable辦法類,這兩個辦法都以R對象的count屬性作為判別的根據。可以看到順序第一次輸入的後果是有序陳列的。當改動R對象的count屬性,順序的輸入後果也發作了改動,而且包括了反復元素。一旦改動了TreeSet集合裡可變元素的屬性,當再視圖刪除該對象時,TreeSet也會刪除失敗(甚至集合中原有的、屬性沒被修正,但與修正後元素相等的元素也無法刪除),所以刪除count
為-2的R對象時,沒有任何元素被刪除;順序可以刪除count為5的R對象,這標明TreeSet可以刪除沒有被修正屬性、且不與其他被修正屬性的對象反復的對象。
總結:與HashSet在處置這些對象時將十分復雜,而且容易出錯。為了讓順序更具強健,引薦HashSet和TreeSet集合中只放入不可變對象。
2、定制排序
TreeSet的自然排序是依據集合元素的大小,TreeSet將他們以升序陳列。假如需求完成定制排序,例如降序,則可以運用Comparator接口。該接口裡包括一個int compare(T o1, T o2)辦法,該辦法用於比擬o1和o2的大小。
假如需求完成定制排序,則需求在創立TreeSet集合對象時,並提供一個Comparator對象與該TreeSet集合關聯,由該Comparator對象擔任集合元素的排序邏輯。
如下順序所示:
class M {
int age;
public M(int age) {
this.age = age;
}
public String toString() {
return "M對象(age:" + age + ")";
}
}
public class TestTreeSet3 {
public static void main(String[] args) {
TreeSet ts = new TreeSet(new Comparator() {
public int compare(Object o1, Object o2) {
M m1 = (M) o1;
M m2 = (M) o2;
if (m1.age > m2.age) {
return -1;
} else if (m1.age == m2.age) {
return 0;
} else {
return 1;
}
}
});
ts.add(new M(5));
ts.add(new M(-3));
ts.add(new M(9));
System.out.println(ts);
}
}
順序運轉後果:
[M對象(age:9), M對象(age:5), M對象(age:-3)]
闡明:
下面順序中創立了一個Comparator接口的匿名外部類對象,該對象擔任ts集合的排序。所以當我們把M對象添加到ts集合中時,無須M類完成Comparable接口,由於此時TreeSet無須經過M對象來比擬大小,而是由與TreeSet關聯的Comparator對象來擔任集合元素的排序。運用定制排序時,TreeSet對集合元素排序時不論集合元素自身的大小,而是由Comparator對象擔任集合元素的排序規則。