【筆試面試】第三波

1、String的split(String regex)方法參數注意點

  使用這個方法時,當我們直接以“.”爲參數時,是會出錯的,如:

String str = "12.03";  
String[] res = str.spilt(".");    //出錯!!!  

  此時,我們得到的res是爲空的(不是null),即str = [];
  因爲String的split(String regex)根據給定的正則表達式的匹配來拆分此字符串,而”.”是正則表達式中的關鍵字,沒有經過轉義split會把它當作一個正則表達式來處理的,需要寫成str.split(“\.”)進行轉義處理。
  正則表達式關鍵字:^$(){}[].?+*|

2、關於hashCode方法

參考文章

我們可以先通過HashMap中hashCode的作用來體驗一下。
我們知道HashMap中是不允許插入重複元素的,如果是插入的同一個元素,會將前面的元素給覆蓋掉,那勢必在HashMap的put方法裏對key值進行了判斷,檢測其是否是同一個對象。其put源碼如下:

public V put(K key, V value) {  
      if (table == EMPTY_TABLE) {    //key的hashCode值放在了table裏面  
          inflateTable(threshold);  
      }  
      if (key == null)  
          return putForNullKey(value);  
      int hash = hash(key);    //計算我們傳進來的key的hashcode值  
      int i = indexFor(hash, table.length);  
      for (Entry<K,V> e = table[i]; e != null; e = e.next) {  
          Object k;  
          if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {    //將傳進來的key的hashcode值於HashMap中的table裏面存放的hashCode值比較  
              V oldValue = e.value;  
              e.value = value;  
              e.recordAccess(this);  
              return oldValue;  
          }  
      }  
      modCount++;  
      addEntry(hash, key, value, i);  
      return null;  
  }  

  可以看到這裏的判斷語句 if (e.hash == hash && ((k = e.key) == key || key.equals(k))),裏面通過&&邏輯運算符相連,先判斷e.hash == hash,即判斷傳進來的key的hashCode值與table中的已有的hashCode值比較,如果不存在該key值,也就不會再去執行&&後面的equals判斷;當已經存在該key值時,再調用equals方法再次確定兩個key值對象是否相同。從這裏可以看出,hashCode方法的存在是爲了減少equals方法的調用次數,從而提高程序效率。
  可以看到,判斷兩個對象是否相同,還是要取決於equals方法,而兩個對象的hashCode值是否相等是兩個對象是否相同的必要條件。所以有以下結論:
  (1)如果兩個對象的hashCode值不等,根據必要條件理論,那麼這兩個對象一定不是同一個對象,即他們的equals方法一定要返回false;
  (2)如果兩個對象的hashCode值相等,這兩個對象也不一定是同一個對象,即他們的equals方法返回值不確定;
 反過來,
  (1)如果equals方法返回true,即是同一個對象,它們的hashCode值一定相等;
  (2)如果equals方法返回false,hashCode值也不一定不相等,即是不確定的;

(hashCode返回的值一般是對象的存儲地址或者與對象存儲地址相關聯的hash散列值)

  然而,很多時候我們可能會重寫equals方法,來判斷這兩個對象是否相等,此時,爲了保證滿足上面的結論,即滿足hashCode值相等是equals返回true的必要條件,我們也需要重寫hashCode方法,以保證判斷兩個對象的邏輯一致(所謂的邏輯一致,是指equals和hashCode方法都是用來判斷對象是否相等)。如下例子:

public class Person {  
    private String name;  
    private int age;  
    public Person(String name,int age){  
        this.name = name;  
        this.age = age;  
    }  
    public String getName() {  
        return name;  
    }  
    public void setName(String name) {  
        this.name = name;  
    }  
    public int getAge() {  
        return age;  
    }  
    public void setAge(int age) {  
        this.age = age;  
    }  

    @Override  
    public boolean equals(Object obj) {  
        return this.name.equals(((Person)obj).name) && this.age== ((Person)obj).age;  
    }  
}  

  在Person裏面重寫了equals方法,但是沒有重寫hashCode方法,如果就我們平時正常來使用的話也不會出什麼問題,如:
  

Person p1 = new Person("lly",18);  
Person p2 = new Person("lly",18);  
System.out.println(p1.equals(p2));    //返回true  

  上面是按照了我們重寫的equals方法,返回了我們想要的值。但是當我們使用HashMap來保存Person對象的時候就會出問題了,如下:  

Person p1 = new Person("lly", 18);  
System.out.println(p1.hashCode());  
HashMap<Person, Integer> hashMap = new HashMap<Person, Integer>();  
hashMap.put(p1, 1);  
System.out.println(hashMap.get(new Person("lly", 18)));    
//此時返回了null,沒有按我們的意願返回1  

  這是因爲,我們沒有重寫Person的hashCode方法,使hashCode方法與我們equals方法的邏輯功能一致,此時的Person對象調用的hashCode方法還是父類的默認實現,即返回的是和對象內存地址相關的int值,這個時候,p1對象和new Person(“lly”,18);對象因爲內存地址不一致,所以其hashCode返回值也是不同的。故HashMap會認爲這是兩個不同的key,故返回null。
   所以,我們想要正確的結果,只需要重寫hashCode方法,讓equals方法和hashCode方法始終在邏輯上保持一致性。

在《Java編程思想》一書中的P495頁有如下的一段話:
  “設計hashCode()時最重要的因素就是:無論何時,對同一個對象調用hashCode()都應該產生同樣的值。如果在將一個對象用put()添加進HashMap時產生一個hashCdoe值,而用get()取出時卻產生了另一個hashCode值,那麼就無法獲取該對象了。所以如果你的hashCode方法依賴於對象中易變的數據,用戶就要當心了,因爲此數據發生變化時,hashCode()方法就會生成一個不同的散列碼”。
  如下一個例子:  

public class Person {  
    private String name;  
    private int age;  
    public Person(String name,int age){  
        this.name = name;  
        this.age = age;  
    }  
    public String getName() {  
        return name;  
    }  
    public void setName(String name) {  
        this.name = name;  
    }  
    public int getAge() {  
        return age;  
    }  
    public void setAge(int age) {  
        this.age = age;  
    }  
    @Override  
    public int hashCode() {  
        return name.hashCode()*37+age;    //hashCode的返回值依賴於對象中的易變數據  
    }  
    @Override  
    public boolean equals(Object obj) {  
        return this.name.equals(((Person)obj).name) && this.age== ((Person)obj).age;  
    }  
}  

  此時我們繼續測試:

Person p1 = new Person("lly", 18);  
System.out.println(p1.hashCode());  
HashMap<Person, Integer> hashMap = new HashMap<Person, Integer>();  
 hashMap.put(p1, 1);  
 p1.setAge(13);   //改變依賴的一個值  
System.out.println(hashMap.get(p1));    //此時還是返回爲null,這是因爲我們p1的hashCode值已經改變了  

  所以,在設計hashCode方法和equals方法的時候,如果對象中的數據易變,則最好在hashCode方法中不要依賴於該字段。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章