在Java中Object類是全部類的父類,其中有幾個須要override的方法好比equals,hashCode和toString等方法。每次寫這幾個方法都要作不少重複性的判斷, 不少類庫提供了覆寫這幾個方法的工具類, Guava也提供了相似的方式。下面咱們來看看Guava中這幾個方法簡單使用。html
equals方法:java
equals是一個常常須要覆寫的方法, 能夠查看Object的equals方法註釋, 對equals有幾個性質的要求:
1. 自反性reflexive:任何非空引用x,x.equals(x)返回爲true;
2. 對稱性symmetric:任何非空引用x和y,x.equals(y)返回true當且僅當y.equals(x)返回true;
3. 傳遞性transitive:任何非空引用x和y,若是x.equals(y)返回true,而且y.equals(z)返回true,那麼x.equals(z)返回true;
4. 一致性consistent:兩個非空引用x和y,x.equals(y)的屢次調用應該保持一致的結果,(前提條件是在屢次比較之間沒有修改x和y用於比較的相關信息);
5. 對於全部非null的值x, x.equals(null)都要返回false。 (若是你要用null.equals(x)也能夠,會報NullPointerException)。ide
當咱們要覆寫的類中某些值可能爲null的時候,就須要對null作不少判斷和分支處理。 使用Guava的Objects.equal方法能夠避免這個問題, 使得equals的方法的覆寫變得更加容易, 並且可讀性強,簡潔優雅。函數
import org.junit.Test; import com.google.common.base.Objects; public class ObjectTest { @Test public void equalTest() { System.out.println(Objects.equal("a", "a")); System.out.println(Objects.equal(null, "a")); System.out.println(Objects.equal("a", null)); System.out.println(Objects.equal(null, null)); } @Test public void equalPersonTest() { System.out.println(Objects.equal(new Person("peida",23), new Person("peida",23))); Person person=new Person("peida",23); System.out.println(Objects.equal(person,person)); } } class Person { public String name; public int age; Person(String name, int age) { this.name = name; this.age = age; } }
運行輸出:工具
true false false true false true
hashCode方法:
性能
當覆寫(override)了equals()方法以後,必須也覆寫hashCode()方法,反之亦然。這個方法返回一個整型值(hash code value),若是兩個對象被equals()方法判斷爲相等,那麼它們就應該擁有一樣的hash code。Object類的hashCode()方法爲不一樣的對象返回不一樣的值,Object類的hashCode值表示的是對象的地址。
hashCode的通常性契約(須要知足的條件)以下:
1.在Java應用的一次執行過程當中,若是對象用於equals比較的信息沒有被修改,那麼同一個對象屢次調用hashCode()方法應該返回同一個整型值。應用的屢次執行中,這個值不須要保持一致,即每次執行都是保持着各自不一樣的值。
2.若是equals()判斷兩個對象相等,那麼它們的hashCode()方法應該返回一樣的值。
3.並無強制要求若是equals()判斷兩個對象不相等,那麼它們的hashCode()方法就應該返回不一樣的值。即,兩個對象用equals()方法比較返回false,它們的hashCode能夠相同也能夠不一樣。可是,應該意識到,爲兩個不相等的對象產生兩個不一樣的hashCode能夠改善哈希表的性能。
寫一個hashCode原本也不是很難,可是Guava提供給咱們了一個更加簡單的方法--Objects.hashCode(Object ...), 這是個可變參數的方法,參數列表能夠是任意數量,因此能夠像這樣使用Objects.hashCode(field1, field2, ..., fieldn)。很是方便和簡潔。測試
import org.junit.Test; import com.google.common.base.Objects; public class ObjectTest { @Test public void hashcodeTest() { System.out.println(Objects.hashCode("a")); System.out.println(Objects.hashCode("a")); System.out.println(Objects.hashCode("a","b")); System.out.println(Objects.hashCode("b","a")); System.out.println(Objects.hashCode("a","b","c")); Person person=new Person("peida",23); System.out.println(Objects.hashCode(person)); System.out.println(Objects.hashCode(person)); } } class Person { public String name; public int age; Person(String name, int age) { this.name = name; this.age = age; } }
128 4066 4096 126145 19313256 19313256
toString()方法:flex
由於每一個類都直接或間接地繼承自Object,所以每一個類都有toString()方法。這個方法是用得最多的, 覆寫得最多, 一個好的toString方法對於調試來講是很是重要的, 可是寫起來確實很不爽。Guava也提供了toString()方法。this
import org.junit.Test; import com.google.common.base.Objects; public class ObjectTest { @Test public void toStringTest() { System.out.println(Objects.toStringHelper(this).add("x", 1).toString()); System.out.println(Objects.toStringHelper(Person.class).add("x", 1).toString()); Person person=new Person("peida",23); String result = Objects.toStringHelper(Person.class) .add("name", person.name) .add("age", person.age).toString(); System.out.print(result); } } class Person { public String name; public int age; Person(String name, int age) { this.name = name; this.age = age; } } //============輸出=============== ObjectTest{x=1} Person{x=1} Person{name=peida, age=23}
compare/compareTo方法:google
CompareTo:compareTo(Object o)方法是java.lang.Comparable<T>接口中的方法,當須要對某個類的對象進行排序時,該類須要實現 Comparable<T>接口的,必須重寫public int compareTo(T o)方法。java規定,若a,b是兩個對象,當a.compareTo(b)>0時,則a大於b,a.compareTo(b)<0時,a<b,即規定對象的比較大小的規則;
compare: compare(Object o1,Object o2)方法是java.util.Comparator<T>接口的方法,compare方法內主要靠定義的compareTo規定的對象大小關係規則來肯定對象的大小。
compareTo方法的通用約定與equals相似:將本對象與指定的對象中止比擬,若是本對象小於、等於、或大於指定對象,則分離返回正數、零、或正數。若是指定的對象類型沒法與本對象中止比擬,則跑出ClassCastException。
對稱性:實現者必須保證對所有的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都有(x.compareTo(z)) == (y.compareTo(z))。
雖不強制要求,但強烈建議(x.compareTo(y) == 0) == (x.equals(y))。通常來講,任何實現了Comparable的類若是違背了這個約定,都應該明白說明。推薦這麼說:「注意:本類擁有天然順序,但與equals不一致」。
第一條指出,若是顛倒兩個比擬對象的比擬順序,就會發生如下狀況:若是第一個對象小於第二個對象,則第二個對象必須大於第一個對象;若是第一個對象等於第二個對象,則第二個對象也必須等於第一個對象;若是第一個對象大於第二個對象,則第二個對象小於第一個對象。
第二條指出,若是第一個對象大於第二個對象,第二個對象大於第三個對象,則第一個大於第三個。
第三條指出,對於兩個相稱的對象,他們與其餘任何對象比擬結果應該雷同。
這三條約定的一個結果是,compareTo方法的等同性測試必須與equals方法滿意雷同的約束條件:自反性、對稱性、傳遞性。因此也存在類同的約束:不能在擴展一個可實例化的類並添加新的值組件時,同時保證compareTo的約定,除非你願意放棄面向對象抽象的優點。能夠用與equals雷同的規避措施:若是想在實現Comparable接口的類中增長一個值組件,就不要擴展它;應該寫一個不相干的類,其中包括第一個類的實例。而後供給一個view方法返回該實例。這樣你就能夠再第二個類上實現任何compareTo方法,同時容許客戶在需要的時候將第二個類當作是第一個類的一個實例。
compareTo約定的最後一段是一個強烈的建議而非真正的約定,即compareTo方法的等同性測試必須與equals方法的結果雷同。若是遵守了這一條,則稱compareTo方法所施加的順序與equals一致;反之則稱爲與equals不一致。固然與equals不一致的compareTo方法仍然是能夠工做的,可是,若是一個有序集合包括了該類的元素,則這個集合可能就不能遵守響應集合接口(Collection、Set、Map)的通用約定。這是由於這些接口的通用約定是基於equals方法的,可是有序集合卻使用了compareTo而非equals來執行。
下面咱們簡單本身實現一個類的compareTo方法:
import org.junit.Test; public class ObjectTest { @Test public void compareTest(){ Person person=new Person("peida",23); Person person1=new Person("aida",25); Person person2=new Person("aida",25); Person person3=new Person("aida",26); Person person4=new Person("peida",26); System.out.println(person.compareTo(person1)); System.out.println(person1.compareTo(person2)); System.out.println(person1.compareTo(person3)); System.out.println(person.compareTo(person4)); System.out.println(person4.compareTo(person)); } } class Person implements Comparable<Person>{ public String name; public int age; Person(String name, int age) { this.name = name; this.age = age; } @Override public int compareTo(Person other) { int cmpName = name.compareTo(other.name); if (cmpName != 0) { return cmpName; } if(age>other.age){ return 1; } else if(age<other.age){ return -1; } return 0; } }
//========輸出===========
15 0 -1 -1 1
上面的compareTo方法,代碼看上去並非十分優雅,若是實體屬性不少,數據類型豐富,代碼可讀性將會不好。在guava裏, 對全部原始類型都提供了比較的工具函數來避免這個麻煩. 好比對Integer, 能夠用Ints.compare()。利用guava的原始類型的compare,咱們對上面的方法作一個簡化,實現compare方法:
class PersonComparator implements Comparator<Person> { @Override public int compare(Person p1, Person p2) { int result = p1.name.compareTo(p2.name); if (result != 0) { return result; } return Ints.compare(p1.age, p2.age); } }
上面的代碼看上去簡單了一點,但仍是不那麼優雅簡單,對此, guava有一個至關聰明的解決辦法, 提供了ComparisonChain:
class Student implements Comparable<Student>{ public String name; public int age; public int score; Student(String name, int age,int score) { this.name = name; this.age = age; this.score=score; } @Override public int compareTo(Student other) { return ComparisonChain.start() .compare(name, other.name) .compare(age, other.age) .compare(score, other.score, Ordering.natural().nullsLast()) .result(); } } class StudentComparator implements Comparator<Student> { @Override public int compare(Student s1, Student s2) { return ComparisonChain.start() .compare(s1.name, s2.name) .compare(s1.age, s2.age) .compare(s1.score, s2.score) .result(); } } }
ComparisonChain是一個lazy的比較過程, 當比較結果爲0的時候, 即相等的時候, 會繼續比較下去, 出現非0的狀況, 就會忽略後面的比較。ComparisonChain實現的compare和compareTo在代碼可讀性和性能上都有很大的提升。
下面來一個綜合應用實例:
import java.util.Comparator; import org.junit.Test; import com.google.common.base.Objects; import com.google.common.collect.ComparisonChain; import com.google.common.collect.Ordering; public class ObjectTest { @Test public void StudentTest(){ Student student=new Student("peida",23,80); Student student1=new Student("aida",23,36); Student student2=new Student("jerry",24,90); Student student3=new Student("peida",23,80); System.out.println("==========equals==========="); System.out.println(student.equals(student2)); System.out.println(student.equals(student1)); System.out.println(student.equals(student3)); System.out.println("==========hashCode==========="); System.out.println(student.hashCode()); System.out.println(student1.hashCode()); System.out.println(student3.hashCode()); System.out.println(student2.hashCode()); System.out.println("==========toString==========="); System.out.println(student.toString()); System.out.println(student1.toString()); System.out.println(student2.toString()); System.out.println(student3.toString()); System.out.println("==========compareTo==========="); System.out.println(student.compareTo(student1)); System.out.println(student.compareTo(student2)); System.out.println(student2.compareTo(student1)); System.out.println(student2.compareTo(student)); } } class Student implements Comparable<Student>{ public String name; public int age; public int score; Student(String name, int age,int score) { this.name = name; this.age = age; this.score=score; } @Override public int hashCode() { return Objects.hashCode(name, age); } @Override public boolean equals(Object obj) { if (obj instanceof Student) { Student that = (Student) obj; return Objects.equal(name, that.name) && Objects.equal(age, that.age) && Objects.equal(score, that.score); } return false; } @Override public String toString() { return Objects.toStringHelper(this) .addValue(name) .addValue(age) .addValue(score) .toString(); } @Override public int compareTo(Student other) { return ComparisonChain.start() .compare(name, other.name) .compare(age, other.age) .compare(score, other.score, Ordering.natural().nullsLast()) .result(); } } class StudentComparator implements Comparator<Student> { @Override public int compare(Student s1, Student s2) { return ComparisonChain.start() .compare(s1.name, s2.name) .compare(s1.age, s2.age) .compare(s1.score, s2.score) .result(); } } //=============運行輸出=========================== ==========equals=========== false false true ==========hashCode=========== -991998617 92809683 -991998617 -1163491205 ==========toString=========== Student{peida, 23, 80} Student{aida, 23, 36} Student{jerry, 24, 90} Student{peida, 23, 80} ==========compareTo=========== 1 1 1 -1