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方法中不要依賴於該字段。