1. 程式人生 > >Guava學習筆記:複寫的Object常用方法

Guava學習筆記:複寫的Object常用方法

  在Java中Object類是所有類的父類,其中有幾個需要override的方法比如equals,hashCode和toString等方法。每次寫這幾個方法都要做很多重複性的判斷, 很多類庫提供了覆寫這幾個方法的工具類, Guava也提供了類似的方式。下面我們來看看Guava中這幾個方法簡單使用。

  equals方法:

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)。

  當我們要覆寫的類中某些值可能為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()方法:

  因為每個類都直接或間接地繼承自Object,因此每個類都有toString()方法。這個方法是用得最多的, 覆寫得最多, 一個好的toString方法對於除錯來說是非常重要的, 但是寫起來確實很不爽。Guava也提供了toString()方法。

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方法:

  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實現的comparecompareTo在程式碼可讀性和效能上都有很大的提高。

  下面來一個綜合應用例項:

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

相關推薦

Guava學習筆記複寫Object常用方法

  在Java中Object類是所有類的父類,其中有幾個需要override的方法比如equals,hashCode和toString等方法。每次寫這幾個方法都要做很多重複性的判斷, 很多類庫提供了覆寫這幾個方法的工具類, Guava也提供了類似的方式。下面我們來看看Guava中這幾個方法簡單使用。   e

Guava學習筆記Optional優雅的使用null

asset 不包含 你在 rgs 命名 靜態 不清晰 ces throw 在我們學習和使用Guava的Optional之前,我們需要來了解一下Java中null。因為,只有我們深入的了解了null的相關知識,我們才能更加深入體會領悟到Guava的Optional設計和使用上

Numpy學習筆記np.array()常用操作

    NumPy是Python語言的一個擴充程式庫。支援高階大量的維度陣列與矩陣運算,此外也針對陣列運算提供大量的數學函式庫。Numpy內部解除了Python的PIL(全域性直譯器鎖),運算效率極好,是大量機器學習框架的基礎庫! 1.np.array建構函式 用法:np.array(

Guava學習筆記Guava cache

  快取,在我們日常開發中是必不可少的一種解決效能問題的方法。簡單的說,cache 就是為了提升系統性能而開闢的一塊記憶體空間。   快取的主要作用是暫時在記憶體中儲存業務系統的資料處理結果,並且等待下次訪問使用。在日常開發的很多場合,由於受限於硬碟IO的效能或者我們自

Guava學習筆記Preconditions優雅的檢驗引數

在日常開發中,我們經常會對方法的輸入引數做一些資料格式上的驗證,以便保證方法能夠按照正常流程執行下去。對於可預知的一些資料上的錯誤,我們一定要做事前檢測和判斷,來避免程式流程出錯,而不是完全通過錯誤處理來保證流程正確執行,畢竟錯誤處理是比較消耗資源的方式。在平常情況下我們對引數的判斷都需要自己來逐個寫方法

【機器學習筆記29】Pandas常用方法備註(補充)

""" 1. Series 是Pandas兩種基本資料結構之一,表徵一維資料 """ so = pd.Series(range(0, 10)) print(so[0:2]) print(so.head(2)) print(so.tail(2)) print(s

Docker學習筆記Docker容器常用命令

       容器是映象的一個執行例項。兩者不同的是,映象是靜態的只讀檔案,而容器帶有執行時需要的可寫檔案層。 一、建立容器        1、新建容器   &nb

Python學習筆記import與常用內建庫

模組 Python模組實質為py檔案,Python在importpy模組時預設會在sys.path所包含的路徑中去尋找,搜尋失敗時會出錯。 匯入整個模組 假設有一個module.py檔案,程式碼如下: var=1 def func():

Guava學習筆記Guava新集合-Table等

  Table  當我們需要多個索引的資料結構的時候,通常情況下,我們只能用這種醜陋的Map<FirstName, Map<LastName, Person>>來實現。為此Guava提供了一個新的集合型別-Table集合型別,來支援這種資料結構的使用場景。Table支援“row”和“

Guava學習筆記Guava新增集合型別-Multiset

  Guava引進了JDK裡沒有的,但是非常有用的一些新的集合型別。所有這些新集合型別都能和JDK裡的集合平滑整合。Guava集合非常精準地實現了JDK定義的介面。Guava中定義的新集合有:  Multiset  SortedMultiset  Multimap  ListMultimap  SetMult

Guava學習筆記Guava新增集合型別-Multimap

  在日常的開發工作中,我們有的時候需要構造像Map<K, List<V>>或者Map<K, Set<V>>這樣比較複雜的集合型別的資料結構,以便做相應的業務邏輯處理。例如: import java.util.ArrayList; import java

Guava學習筆記Immutable(不可變)集合

  不可變集合,顧名思義就是說集合是不可被修改的。集合的資料項是在建立的時候提供,並且在整個生命週期中都不可改變。   為什麼要用immutable物件?immutable物件有以下的優點:     1.對不可靠的客戶程式碼庫來說,它使用安全,可以在未受信任的類庫中安全的使用這些物件    2.執行緒安全

Guava學習筆記EventBus

  EventBus是Guava的事件處理機制,是設計模式中的觀察者模式(生產/消費者程式設計模型)的優雅實現。對於事件監聽和釋出訂閱模式,EventBus是一個非常優雅和簡單解決方案,我們不用建立複雜的類和介面層次結構。   Observer模式是比較常用的設計模式之一,雖然有時候在具體程式碼裡,它不一定

Guava學習筆記簡化異常處理的Throwables類

  有時候, 當我們我們捕獲異常, 並且像把這個異常傳遞到下一個try/catch塊中。Guava提供了一個異常處理工具類, 可以簡單地捕獲和重新丟擲多個異常。例如: import java.io.IOException; import org.junit.Test; import com.goog

Guava學習筆記Range

  在Guava中新增了一個新的型別Range,從名字就可以瞭解到,這個是和區間有關的資料結構。從Google官方文件可以得到定義:Range定義了連續跨度的範圍邊界,這個連續跨度是一個可以比較的型別(Comparable type)。比如1到100之間的整型資料。   在數學裡面的範圍是有邊界和無邊界之分

Guava學習筆記Guava新增集合型別-Bimap

  BiMap提供了一種新的集合型別,它提供了key和value的雙向關聯的資料結構。  通常情況下,我們在使用Java的Map時,往往是通過key來查詢value的,但是如果出現下面一種場景的情況,我們就需要額外編寫一些程式碼了。首先來看下面一種表示標識序號和檔名的map結構。 @Test

Guava學習筆記Ordering犀利的比較器

  Ordering是Guava類庫提供的一個犀利強大的比較器工具,Guava的Ordering和JDK Comparator相比功能更強。它非常容易擴充套件,可以輕鬆構造複雜的comparator,然後用在容器的比較、排序等操作中。   本質上來說,Ordering 例項無非就是一個特殊的Comparat

學習筆記幾種注入方法

//member template class Integer{ public: template <int N> void multiple(); private: int i_; }; template <> void Int

kotlin學習筆記object關鍵字介紹與java中的靜態變數與靜態方法的實現以及@JvmField和@JvmStatic的使用

在java中,靜態變數和靜態方法是我們經常需要用到的東西,但是我們在kotlin中,並不能找到static關鍵字。其實目前在kotlin中,也的確是static概念的,那麼我們該如何在kotlin中實現靜態變數和靜態方法呢?這時就要用到kotlin中的obje

JavaScript學習筆記數組的sort()和reverse()方法

content 參數 n) strong true 知識 efi 調整 數字 在實際的業務當中,很多時候要對定義好的數組重新排序。在JavaScript中自帶了兩個方法,可以對數組進行排序操作。這兩個方法就是sort()和reve