對象排序
List l可以如下排序。
Collections.sort(l);
如果List包含String元素,它將按字母順序排序,如果它由Date元素組成,它將按時間順序排序,這是怎么發生的?String和Date都實現了Comparable接口,Comparable實現為類提供了自然的順序,允許該類的對象自動排序,下表總結了一些實現Comparable的更重要的Java平臺類。
類 | 自然排序 |
---|---|
Byte | 有符號數字 |
Character | 無符號數字 |
Long | 有符號數字 |
Integer | 有符號數字 |
Short | 有符號數字 |
Double | 有符號數字 |
Float | 有符號數字 |
BigInteger | 有符號數字 |
BigDecimal | 有符號數字 |
Boolean | Boolean.FALSE < Boolean.TRUE |
File | 依賴于系統的路徑名稱上的字典 |
String | 接字母順序 |
Date | 按時間順序 |
CollationKey | 特定于語言環境的字典 |
如果你嘗試對列表進行排序,其中的元素未實現Comparable,Collections.sort(list)將拋出ClassCastException,類似地,如果你嘗試使用comparator對其元素無法相互比較的列表進行排序,則Collections.sort(list, comparator)將拋出ClassCastException。雖然不同類型的元素可以相互比較,但這里列出的類別都不允許進行類間比較。
如果你只想對可比較元素的列表進行排序或創建它們的已排序集合,那么你真正需要了解Comparable接口的所有內容,如果要實現自己的Comparable類型,下一部分將是你感興趣的。
編寫自己的Comparable類型Comparable接口包含以下方法。
public interface Comparable{ public int compareTo(T o); }
compareTo方法將接收對象與指定對象進行比較,并返回負整數、0或正整數,具體取決于接收對象是否小于、等于或大于指定對象,如果無法將指定的對象與接收對象進行比較,則該方法將拋出ClassCastException。
以下表示人名的類實現了Comparable。
import java.util.*; public class Name implements Comparable{ private final String firstName, lastName; public Name(String firstName, String lastName) { if (firstName == null || lastName == null) throw new NullPointerException(); this.firstName = firstName; this.lastName = lastName; } public String firstName() { return firstName; } public String lastName() { return lastName; } public boolean equals(Object o) { if (!(o instanceof Name)) return false; Name n = (Name) o; return n.firstName.equals(firstName) && n.lastName.equals(lastName); } public int hashCode() { return 31*firstName.hashCode() + lastName.hashCode(); } public String toString() { return firstName + " " + lastName; } public int compareTo(Name n) { int lastCmp = lastName.compareTo(n.lastName); return (lastCmp != 0 ? lastCmp : firstName.compareTo(n.firstName)); } }
為了使前面的例子簡短,該類有些限制:它不支持中間名,它要求名字和姓氏,并且它不以任何方式國際化,盡管如此,它還說明了以下要點:
Name對象是不可變的,在所有其他條件相同的情況下,不可變類型是解決問題的方法,特別是對于將作為集合中的元素或Map中的鍵使用的對象,如果你在集合中修改元素或鍵,這些集合將會中斷。
構造函數檢查其參數是否為null,這可以確保所有Name對象都格式正確,這樣其他任何方法都不會拋出NullPointerException。
hashCode方法被重新定義,這對于重新定義equals方法的任何類都是必不可少的(等同對象必須具有相同的哈希碼)。
如果指定的對象為null或類型不合適,則equals方法返回false,compareTo方法在這些情況下拋出運行時異常,這兩種行為都是各自方法的一般契約所要求的。
toString方法已重新定義,因此它以人類可讀的形式打印Name,這總是一個好主意,特別是對于要放入集合的對象,各種集合類型的toString方法依賴于其元素、鍵和值的toString方法。
由于本節是關于元素排序的,讓我們再談談Name的compareTo方法,它實現了標準的名稱排序算法,其中姓氏優先于名字,這正是你想要的自然順序,如果自然順序不自然,那將會非?;靵y!
看看compareTo是如何實現的,因為它非常經典,首先,比較對象的最重要部分(在本例中為姓氏),通常,你可以只使用部分類型的自然順序,在這種情況下,該部分是一個字符串,自然(詞典)排序正是所要求的。如果比較的結果不是0(代表相等),那么就完成了:你只需返回結果。如果最重要的部分相同,則繼續比較下一個最重要的部分,在這種情況下,只有兩個部分 — 名字和姓氏。如果有更多的部分,你會以明顯的方式進行,比較部分,直到你發現兩個不相等或你正在比較最不重要的部分,此時你將返回比較的結果。
為了說明這一切都是有效的,這里有一個程序,它構建了一個名稱列表并對它們進行排序。
import java.util.*; public class NameSort { public static void main(String[] args) { Name[] nameArray = { new Name("John", "Smith"), new Name("Karl", "Ng"), new Name("Jeff", "Smith"), new Name("Tom", "Rich") }; Listnames = Arrays.asList(nameArray); Collections.sort(names); System.out.println(names); } }
如果你運行這個程序,這是它打印的內容。
[Karl Ng, Tom Rich, Jeff Smith, John Smith]
compareTo方法的行為有四個限制,我們現在不會討論它們,因為它們相當技術性和枯燥,最好留在API文檔中,實現Comparable的所有類都遵守這些限制非常重要,因此如果你正在編寫實現它的類,請閱讀Comparable的文檔。嘗試對違反限制的對象列表進行排序具有未定義的行為,從技術上講,這些限制確保自然順序是實現它的類的對象的總順序,這對于確保明確定義排序是必要的。
Comparators如果你想按一些對象的自然順序以外的順序排序,該怎么辦?或者,如果要對某些未實現Comparable的對象進行排序,該怎么辦?要執行上述任一操作,你需要提供Comparator — 一個封裝排序的對象,與Comparable接口一樣,Comparator接口由單個方法組成。
public interface Comparator{ int compare(T o1, T o2); }
compare方法比較它的兩個參數,返回一個負整數、0或一個正整數,具體取決于第一個參數是小于、等于還是大于第二個參數,如果其中一個參數的Comparator類型不合適,則compare方法將拋出ClassCastException。
關于Comparable的大部分內容也適用于Comparator,編寫compare方法與編寫compareTo方法幾乎完全相同,只是前者將兩個對象作為參數傳入,由于同樣的原因,compare方法必須遵守與Comparable的compareTo方法相同的四個技術限制 — Comparator必須對它所比較的??對象產生總順序。
假設你有一個名為Employee的類,如下所示。
public class Employee implements Comparable{ public Name name() { ... } public int number() { ... } public Date hireDate() { ... } ... }
讓我們假設Employee實例的自然順序是員工姓名上的Name排序(如上例所定義),不幸的是,老板要求按照資歷順序列出員工名單。這意味著我們必須做一些工作,但并不多,以下程序將生成所需的列表。
import java.util.*; public class EmpSort { static final ComparatorSENIORITY_ORDER = new Comparator () { public int compare(Employee e1, Employee e2) { return e2.hireDate().compareTo(e1.hireDate()); } }; // Employee database static final Collection employees = ... ; public static void main(String[] args) { List e = new ArrayList (employees); Collections.sort(e, SENIORITY_ORDER); System.out.println(e); } }
程序中的Comparator相當簡單,它依賴于應用于hireDate訪問器方法返回的值的Date的自然順序,注意,Comparator將第二個參數的雇用日期傳遞給第一個參數,而不是反過來,原因是最近招聘的員工級別最低,按雇用日期順序排序會使該名單的資歷順序相反,人們有時用來達到這種效果的另一種技術是保持參數順序,但要否定比較的結果。
// Don"t do this!! return -r1.hireDate().compareTo(r2.hireDate());
你應該總是使用前一種技術來支持后者,因為后者不能保證有效,這樣做的原因是compareTo方法可以返回任何負整數,如果它的參數小于調用它的對象。有一個負整型數在被否定時仍然是負的,盡管這看起來很奇怪。
-Integer.MIN_VALUE == Integer.MIN_VALUE
上一個程序中的Comparator可以很好地對List進行排序,但確實存在一個缺陷:它不能用于排序已排序的集合,例如TreeSet,因為它生成的順序與equals不兼容,這意味著這個Comparator相當于equals方法所沒有的對象。特別是,在同一天雇傭的任何兩名員工將相等,當你對List進行排序時,這并不重要,但是當你使用Comparator來排序一個已排序的集合時,它是致命的,如果你使用此Comparator將在同一日期雇用的多名員工插入到TreeSet中,則只會將第一個員工添加到該集合中,第二個將被視為重復元素,將被忽略。
要解決此問題,只需調整Comparator,以便生成與equals兼容的排序,換句話說,調整它以便在使用compare時看到相同的唯一元素是那些在使用equals進行比較時也被視為相等的元素。執行此操作的方法是執行兩部分比較(像對于Name),其中第一部分是我們感興趣的部分 — 在這種情況下,是雇用日期 — 第二部分是唯一標識對象的屬性,員工編號在這里是明顯的屬性,這是比較器的結果。
static final ComparatorSENIORITY_ORDER = new Comparator () { public int compare(Employee e1, Employee e2) { int dateCmp = e2.hireDate().compareTo(e1.hireDate()); if (dateCmp != 0) return dateCmp; return (e1.number() < e2.number() ? -1 : (e1.number() == e2.number() ? 0 : 1)); } };
最后一點:你可能想要使用更簡單的方法替換Comparator中的最終return語句:
return e1.number() - e2.number();
除非你絕對確定沒有人會有負的員工編號,否則不要這樣做!這個技巧通常不起作用,因為帶符號整數類型不夠大,不能表示兩個任意帶符號整數的差,如果i是一個大的正整數且j是一個大的負整數,i - j將溢出并返回一個負整數,由此產生的comparator違反了我們一直在討論的四個技術限制之一(傳遞性)并產生可怕的、微妙的錯誤,這不是純粹的理論問題。
上一篇:Map接口 下一篇:SortedSet接口文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/73965.html
集合接口 核心集合接口封裝了不同類型的集合,如下圖所示,這些接口允許獨立于其表示的細節來操縱集合,核心集合接口是Java集合框架的基礎,如下圖所示,核心集合接口形成層次結構。 showImg(https://segmentfault.com/img/bVbntJW?w=402&h=146); Set是一種特殊的Collection,SortedSet是一種特殊的Set,依此類推,另請注意,層次結構...
摘要:從行,可以看出字符串的存儲結構是字符數組。如果不相等,則返回兩字符的編碼值的差值第行當前字符串和另一個字符串,依次字符比較。如果均相等,則返回兩個字符串長度的差值所以要排序,肯定先有比較能力,即實現接口。摘要: 原創出處 https://www.bysocket.com 「公眾號:泥瓦匠BYSocket 」歡迎關注和轉載,保留摘要,謝謝!這是泥瓦匠的第103篇原創《程序兵法:Java Str...
Queue接口 Queue是在處理之前保存元素的集合,除了基本的Collection操作外,隊列還提供額外的插入、刪除和檢查操作,Queue接口如下。 public interface Queue extends Collection { E element(); boolean offer(E e); E peek(); E poll(); E remov...
摘要:簡明教程原文譯者黃小非來源簡明教程并沒有沒落,人們很快就會發現這一點歡迎閱讀我編寫的介紹。編譯器會自動地選擇合適的構造函數來匹配函數的簽名,并選擇正確的構造函數形式。 Java 8 簡明教程 原文:Java 8 Tutorial 譯者:ImportNew.com - 黃小非 來源:Java 8簡明教程 ? Java并沒有沒落,人們很快就會發現這一點 歡迎閱讀我編寫的Java ...
默認方法 接口部分描述了一個涉及計算機控制汽車制造商的例子,他們發布了行業標準接口,描述了可以調用哪些方法來操作他們的汽車,如果那些計算機控制的汽車制造商為他們的汽車添加新的功能,如飛行,該怎么辦?這些制造商需要指定新的方法,以使其他公司(如電子制導儀器制造商)能夠使其軟件適應飛行汽車,這些汽車制造商將在哪里聲明這些與飛行有關的新方法?如果他們將它們添加到原始接口,那么實現了這些接口的程序員將不得...
閱讀 1469·2021-11-22 14:44
閱讀 2848·2021-11-16 11:44
閱讀 3214·2021-10-13 09:40
閱讀 1993·2021-10-08 10:04
閱讀 2368·2021-09-24 10:28
閱讀 2916·2021-09-06 15:02
閱讀 2965·2019-08-30 15:52
閱讀 2400·2019-08-30 13:20