日期:2017/11/19
接口 Comparable<T> 與 接口 Comparator<T> 的差別
一、接口 Comparable
java.lang 接口 Comparable
類型參數:
T
- 可以與此對象進行比較的那些對象的類型
所有已知子接口:
Delayed,
Name,
RunnableScheduledFuture<V>,
ScheduledFuture<V>
所有已知實作類:
Authenticator.RequestorType,
BigDecimal,
BigInteger,
Boolean,
Byte,
ByteBuffer,
Calendar,
Character,
CharBuffer,
Charset,
ClientInfoStatus,
CollationKey,
Component.BaselineResizeBehavior,
CompositeName,
CompoundName,
Date,
Date,
Desktop.Action,
Diagnostic.Kind,
Dialog.ModalExclusionType,
Dialog.ModalityType,
Double,
DoubleBuffer,
DropMode,
ElementKind,
ElementType,
Enum,
File,
Float,
FloatBuffer,
Formatter.BigDecimalLayoutForm,
FormSubmitEvent.MethodType,
GregorianCalendar,
GroupLayout.Alignment,
IntBuffer,
Integer,
JavaFileObject.Kind,
JTable.PrintMode,
KeyRep.Type,
LayoutStyle.ComponentPlacement,
LdapName,
Long,
LongBuffer,
MappedByteBuffer,
MemoryType,
MessageContext.Scope,
Modifier,
MultipleGradientPaint.ColorSpaceType,
MultipleGradientPaint.CycleMethod,
NestingKind,
Normalizer.Form,
ObjectName,
ObjectStreamField,
Proxy.Type,
Rdn,
Resource.AuthenticationType,
RetentionPolicy,
RoundingMode,
RowFilter.ComparisonType,
RowIdLifetime,
RowSorterEvent.Type,
Service.Mode,
Short,
ShortBuffer,
SOAPBinding.ParameterStyle,
SOAPBinding.Style,
SOAPBinding.Use,
SortOrder,
SourceVersion,
SSLEngineResult.HandshakeStatus,
SSLEngineResult.Status,
StandardLocation,
String,
SwingWorker.StateValue,
Thread.State,
Time,
Timestamp,
TimeUnit,
TrayIcon.MessageType,
TypeKind,
URI,
UUID,
WebParam.Mode,
XmlAccessOrder,
XmlAccessType,
XmlNsForm
public interface Comparable<T>
此接口強行對實作它的每個類的對象進行整體排序。這種排序被稱為類的自然排序,類的 compareTo 方法被稱為它的自然比較方法。
實作此接口的對象清單(和數組)可以通過 Collections.sort(和 Arrays.sort)進行自動排序。實作此接口的對象可以用作有序映射中的鍵或有序集合中的元素,無需指定比較器。
對于類 C 的每一個 e1 和 e2 來說,當且僅當 e1.compareTo(e2) == 0 與 e1.equals(e2) 具有相同的 boolean 值時,類 C的自然排序才叫做與 equals 一緻。注意,null 不是任何類的執行個體,即使 e.equals(null) 傳回 false,e.compareTo(null) 也将抛出 NullPointerException。
建議(雖然不是必需的)最好使自然排序與 equals 一緻。這是因為在使用自然排序與 equals 不一緻的元素(或鍵)時,沒有顯式比較器的有序集合(和有序映射表)行為表現“怪異”。尤其是,這樣的有序集合(或有序映射表)違背了根據 equals
例如,如果将兩個鍵 a 和 b 添加到沒有使用顯式比較器的有序集合中,使 (!a.equals(b) && a.compareTo(b) == 0),那麼第二個 add 操作将傳回 false(有序集合的大小沒有增加),因為從有序集合的角度來看,a 和 b
實際上,所有實作 Comparable 的 Java 核心類都具有與 equals 一緻的自然排序。java.math.BigDecimal 是個例外,它的自然排序将值相等但精确度不同的 BigDecimal
從數學上講,定義給定類 C 上自然排序的關系式 如下:
{(x, y)|x.compareTo(y) <= 0}。
整體排序的
商
是:
{(x, y)|x.compareTo(y) == 0}。
它直接遵循
compareTo
的協定,商是
C
的
等價關系
,自然排序是
C
的
整體排序
。當說到類的自然排序
與 equals 一緻
時,是指自然排序的商是由類的
equals(Object)
方法定義的等價關系。
{(x, y)|x.equals(y)}。
此接口是 Java Collections Framework 的成員。
方法摘要 | |
| 比較此對象與指定對象的順序。 |
方法詳細資訊 |
compareTo
int compareTo(T o)
實作類必須確定對于所有的 x 和 y 都存在 sgn(x.compareTo(y)) == -sgn(y.compareTo(x)) 的關系。(這意味着如果 y.compareTo(x) 抛出一個異常,則 x.compareTo(y)
實作類還必須確定關系是可傳遞的:(x.compareTo(y)>0 && y.compareTo(z)>0) 意味着 x.compareTo(z)>0。
最後,實作者必須確定 x.compareTo(y)==0 意味着對于所有的 z,都存在 sgn(x.compareTo(z)) == sgn(y.compareTo(z))。 強烈推薦 (x.compareTo(y)==0) == (x.equals(y)) 這種做法,但并不是 嚴格要求這樣做。一般來說,任何實作 Comparable
在前面的描述中,符号 sgn(expression) 指定 signum 數學函數,該函數根據 expression 的值是負數、零還是正數,分别傳回 -1、0 或 1
參數:
o
- 要比較的對象。
傳回:
負整數、零或正整數,根據此對象是小于、等于還是大于指定對象。
抛出:
ClassCastException
- 如果指定對象的類型不允許它與此對象進行比較。
例子1:
package com.code.CompareInterface.ComparableInterface;
//類型 Person 必須實作繼承的抽象方法 Comparable<String>.compareTo(String)
public class Person implements Comparable<Person>{
private String name;
private Integer age;
public Person(){
}
public Person (String name ,Integer age){
this.name = name;
this.age = age;
}
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
public void setAge(Integer age){
this.age = age;
}
public Integer getAge(){
return age;
}
@Override
public int compareTo(Person p) {
//auto 比較age
if(this.age > p.age){
return (this.age - p.age);
}if(this.age < p.age){
return (this.age - p.age);
}
//auto 比較name
if(this.name.compareTo(p.name) > 0){
return 1;
}
if(this.name.compareTo(p.name) < 0){
return -1;
}
return 0;
}
}
package com.code.CompareInterface.ComparableInterface;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.ListIterator;
public class ComparableTest {
public static void main(String [] args){
Person p1 = new Person("AA",19);
Person p2 = new Person("aa",13);
Person p3 = new Person("CC",13);
Person p4 = new Person("DD",14);
//不能執行個體化類型 List<Person>
List<Person> list = new ArrayList<Person>();
list.add(p1);
list.add(p2);
list.add(p3);
list.add(p4);
ListIterator it = list.listIterator();
System.out.println(list.size());
while(it.hasNext()){
Person s = new Person();
s = (Person) it.next();
System.out.println(s.getName()+ "---"+s.getAge());
}
System.out.println("------------------------");
//回到正題,進行排序
Collections.sort(list);
for(Person p : list){
System.out.println(p.getName()+"---"+p.getAge());
}
}
}
輸出結果:
4
AA---19
aa---13
CC---13
DD---14
------------------------
CC---13
aa---13
DD---14
AA---19
解釋:(1)從Person.java 重寫的compareTo()可以看出先是比較age值大小,再以name的Unicode序号作為排序依據。
二、接口 Comparator
java.util 接口 Comparator
類型參數:
T
- 此 Comparator 可以比較的對象類型
所有已知實作類:
Collator,
RuleBasedCollator
public interface Comparator<T>
強行對某個對象 collection 進行整體排序 的比較函數。可以将 Comparator 傳遞給 sort 方法(如 Collections.sort 或 Arrays.sort),進而允許在排序順序上實作精确控制。還可以使用 Comparator 來控制某些資料結構(如有序 set或有序映射)的順序,或者為那些沒有自然順序的對象 collection 提供排序。
當且僅當對于一組元素 S 中的每個 e1 和 e2 而言,c.compare(e1, e2)==0 與 e1.equals(e2) 具有相等的布爾值時,Comparator c 強行對 S 進行的排序才叫做與 equals 一緻 的排序。
當使用具有與 equals 不一緻的強行排序能力的 Comparator 對有序 set(或有序映射)進行排序時,應該小心謹慎。假定一個帶顯式 Comparator c 的有序 set(或有序映射)與從 set S 中抽取出來的元素(或鍵)一起使用。如果 c 強行對 S 進行的排序是與 equals 不一緻的,那麼有序 set(或有序映射)将是行為“怪異的”。尤其是有序 set(或有序映射)将違背根據 equals
例如,假定使用 Comparator
c
将滿足
(a.equals(b) && c.compare(a, b) != 0)
的兩個元素
a
和
b
添加到一個空
TreeSet
中,則第二個
add
操作将傳回 true(樹 set 的大小将會增加),因為從樹 set 的角度來看,
a
和
b
是不相等的,即使這與 Set.add 方法的規範相反。
注:通常來說,讓 Comparator 也實作 java.io.Serializable 是一個好主意,因為它們在可序列化的資料結構(像 TreeSet、TreeMap)中可用作排序方法。為了成功地序列化資料結構,Comparator(如果已提供)必須實作 Serializable。
在算術上,定義給定 Comparator c 對給定對象 set S 實施強行排序 的關系式 為:
{(x, y) such that c.compare(x, y) <= 0}.
此整體排序的
商 (quotient)
為:
{(x, y) such that c.compare(x, y) == 0}.
它直接遵循
compare
的協定,商是
S
上的
等價關系
,強行排序是
S
上的
整體排序
。當我們說
c
強行對
S
的排序是
與 equals 一緻
的時,意思是說排序的商是對象的
equals(Object)
方法所定義的等價關系:
{(x, y) such that x.equals(y)}.
此接口是 Java Collections Framework 的成員。
方法摘要 | |
| 比較用來排序的兩個參數。 |
| 訓示某個其他對象是否“等于”此 Comparator。 |
方法詳細資訊 |
compare
int compare(T o1,
T o2)
在前面的描述中,符号 sgn(expression) 表示 signum 數學函數,根據 expression 的值為負數、0 還是正數,該函數分别傳回 -1、0 或 1。
實作程式必須確定對于所有的 x 和 y 而言,都存在 sgn(compare(x, y)) == -sgn(compare(y, x))。(這意味着當且僅當 compare(y, x) 抛出異常時 compare(x, y)
實作程式還必須確定關系是可傳遞的:((compare(x, y)>0) && (compare(y, z)>0)) 意味着 compare(x, z)>0。
最後,實作程式必須確定 compare(x, y)==0 意味着對于所有的 z 而言,都存在 sgn(compare(x, z))==sgn(compare(y, z))。
雖然這種情況很普遍,但并不 嚴格要求 (compare(x, y)==0) == (x.equals(y))。一般說來,任何違背這個條件的 Comparator 都應該清楚地指出這一事實。推薦的語言是“注意:此 Comparator 強行進行與 equals 不一緻的排序。”
參數:
o1
- 要比較的第一個對象。
o2
- 要比較的第二個對象。
傳回:
根據第一個參數小于、等于或大于第二個參數分别傳回負整數、零或正整數。
抛出:
ClassCastException
- 如果參數的類型不允許此 Comparator 對它們進行比較。
equals
boolean equals(Object obj)
訓示某個其他對象是否“等于”此 Comparator。此方法必須遵守
Object.equals(Object) 的正常協定。此外,
僅當 指定的對象也是一個 Comparator,并且強行實施與此 Comparator 相同的排序時,此方法才傳回
true。是以,
comp1.equals(comp2)
意味着對于每個對象引用
o1 和
o2 而言,都存在
sgn(comp1.compare(o1, o2))==sgn(comp2.compare(o1, o2))。
注意,不 重寫 Object.equals(Object) 方法總是 安全的。然而,在某些情況下,重寫此方法可以允許程式确定兩個不同的 Comparator 是否強行實施了相同的排序,進而提高性能。
覆寫:
類
Object
中的
equals
參數:
obj
- 要進行比較的引用對象。
傳回:
僅當指定的對象也是一個 Comparator,并且強行實施與此 Comparator 相同的排序時才傳回
true
。
另請參見:
Object.equals(Object),
Object.hashCode()
例子2:
package com.code.CompareInterface.Comparator;
import java.util.Comparator;
//類型 Person 必須實作繼承的抽象方法 Comparator<Person>.compare(Person, Person)
public class Person implements Comparator<Person>{
private String name;
private Integer age;
public Person(){
}
public Person (String name ,Integer age){
this.name = name;
this.age = age;
}
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
public void setAge(Integer age){
this.age = age;
}
public Integer getAge(){
return age;
}
@Override
public int compare(Person p1, Person p2) {
if(p1.getAge() > p2.getAge()){
return 1;
}else if(p1.getAge() < p2.getAge()){
return -1;
}else{
if(p1.getName().compareTo(p2.getName()) > 0){
return 1;
}else if(p1.getName().compareTo(p2.getName()) > 0){
return -1;
}else{
return 0;
}
}
}
}
package com.code.CompareInterface.Comparator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.ListIterator;
import com.code.CompareInterface.ComparableInterface.Person;
public class ComparatorTest {
public static void main(String[] args) {
Person p1 = new Person("AA",19);
Person p2 = new Person("aa",13);
Person p3 = new Person("CC",13);
Person p4 = new Person("DD",14);
//不能執行個體化類型 List<Person>
List<Person> list = new ArrayList<Person>();
list.add(p1);
list.add(p2);
list.add(p3);
list.add(p4);
ListIterator it = list.listIterator();
System.out.println(list.size());
while(it.hasNext()){
Person s = new Person();
s = (Person) it.next();
System.out.println(s.getName()+ "---"+s.getAge());
}
System.out.println("------------------------");
//差別與Compearable接口
Collections.sort(list);
for(Person p : list){
System.out.println(p.getName()+"---"+p.getAge());
}
}
}
輸出結果:
4
AA---19
aa---13
CC---13
DD---14
------------------------
CC---13
aa---13
DD---14
AA---19
驗證了:使用兩種接口達到一樣的效果。