關於Java中的hashCode和equals方法

直接上源碼!java

public native int hashCode();
/**
* Returns a hash code value for the object. This method is
* supported for the benefit of hash tables such as those provided by
* {@link java.util.HashMap}.
* <p>
* The general contract of {@code hashCode} is:
* <ul>
* <li>Whenever it is invoked on the same object more than once during
* an execution of a Java application, the {@code hashCode} method
* must consistently return the same integer, provided no information
* used in {@code equals} comparisons on the object is modified.
* This integer need not remain consistent from one execution of an
* application to another execution of the same application.
* <li>If two objects are equal according to the {@code equals(Object)}
* method, then calling the {@code hashCode} method on each of
* the two objects must produce the same integer result.
* <li>It is <em>not</em> required that if two objects are unequal
* according to the {@link java.lang.Object#equals(java.lang.Object)}
* method, then calling the {@code hashCode} method on each of the
* two objects must produce distinct integer results. However, the
* programmer should be aware that producing distinct integer results
* for unequal objects may improve the performance of hash tables.
* </ul>
* <p>
* As much as is reasonably practical, the hashCode method defined by
* class {@code Object} does return distinct integers for distinct
* objects. (This is typically implemented by converting the internal
* address of the object into an integer, but this implementation
* technique is not required by the
* Java&trade; programming language.)
*
* @return a hash code value for this object.
* @see java.lang.Object#equals(java.lang.Object)
* @see java.lang.System#identityHashCode
*/

官方文檔第一句話就說了,這個hashCode方法主要是爲了哈希表而存在的,像HashSet,HashTable和HashMap都是使用哈希表的形式存儲數據(Key Value),而hashCode計算出的hash值就能夠惟一肯定一個元素,有了hash值即可以快速定位元素,提升哈希表的性能。下面是hashCode規定的幾點:程序員

  • 在同一個Java應用程序中,只要是同一個對象,不管你調用hashCode方法多少次,它的返回值都應該是相同的。在不一樣的java應用程序中,這個返回值能夠不一樣。
  • 若是兩個對象經過equals方法判斷出來是相同的,那麼這兩個對象調用hashCode方法的返回值也必須相同。
  • 若是兩個對象經過調用頂級父類(Object)的equals方法判斷出來是不相等的,那麼這兩個對象分別調用hashCode方法的返回值也必須是不一樣的。
  • 可是,做爲程序員的咱們能夠在子類中重寫這個方法,使得只要是對象中的值相等,那麼這兩個對象就相等,可是這可能會下降hash表的性能。(根據實際需求來判斷是否是要重寫吧)

基於這些緣由,object類中的hashCode方法對於不一樣的對象必須返回不一樣的值(這是由內部轉換方式決定的,一般這個值就是對象在JVM中的實際地址)app

那這個值的取值確定不都是對象實際所在的地址吧!好比說:8個基本數據類型的包裝類的hashCodeide

  Boolean Byte Short Integer Long Character Float Doubles性能

下面咱們來查看他們是怎麼重寫父類的方法的flex

  Boolean@Overrideui

public int hashCode() {
return Boolean.hashCode(value);
}

/**
* Returns a hash code for a {@code boolean} value; compatible with
* {@code Boolean.hashCode()}.
*
* @param value the value to hash
* @return a hash code value for a {@code boolean} value.
* @since 1.8
*/
public static int hashCode(boolean value) {
return value ? 1231 : 1237;
}
能夠看出,Boolean類型的變量,比較的是布爾值,若是都爲true,那麼返回1231,若是爲false,那麼返回1237,能夠說只要
是Boolean類型的對象,只要值相同,那麼他們就相同。

  Bytethis

@Override
public int hashCode() {
return Byte.hashCode(value);
}

/**
* Returns a hash code for a {@code byte} value; compatible with
* {@code Byte.hashCode()}.
*
* @param value the value to hash
* @return a hash code value for a {@code byte} value.
* @since 1.8
*/
public static int hashCode(byte value) {
return (int)value;
}
對於Byte類型的對象,hashCode的值就是他強轉爲int類型後的值

Integer,Short同理都是轉化爲int類型後的值

  Long
@Override
public int hashCode() {
return Long.hashCode(value);
}

/**
* Returns a hash code for a {@code long} value; compatible with
* {@code Long.hashCode()}.
*
* @param value the value to hash
* @return a hash code value for a {@code long} value.
* @since 1.8
*/
public static int hashCode(long value) {
return (int)(value ^ (value >>> 32));
}
返回值是其自己和他帶符號右移後的數相異或獲得的值再強轉爲int型。

  Character
直接調用Object類的hashCode方法
  Float
public static int hashCode(float value) {
return floatToIntBits(value);
}
public static int floatToIntBits(float value) {
int result = floatToRawIntBits(value);
// Check for NaN based on values of bit fields, maximum
// exponent and nonzero significand.
if ( ((result & FloatConsts.EXP_BIT_MASK) ==
FloatConsts.EXP_BIT_MASK) &&
(result & FloatConsts.SIGNIF_BIT_MASK) != 0)
result = 0x7fc00000;
return result;
}public static native int floatToRawIntBits(float value);
API上這樣說:返回這個浮點對象的哈希代碼。其結果是整數位表示,與方法floatToIntBits(float)所產生的同樣,是由這個
浮點對象表示的原始浮點的值

  Double
與Floate型的差很少
API上這樣說:返回此Double對象的哈希代碼。結果是惟一的或兩個半整數位表示的兩個部分,徹底由方法
doubleToLongBits(double)所產生

經過以上的這些話,能夠很明確這個方法確定和equals方法分不開。這不,源碼中緊接着就是equals方法spa

public boolean equals(Object obj) {
return (this == obj);
}
/**
* Indicates whether some other object is "equal to" this one.
* <p>
* The {@code equals} method implements an equivalence relation
* on non-null object references:
* <ul>
* <li>It is <i>reflexive</i>: for any non-null reference value
* {@code x}, {@code x.equals(x)} should return
* {@code true}.
* <li>It is <i>symmetric</i>: for any non-null reference values
* {@code x} and {@code y}, {@code x.equals(y)}
* should return {@code true} if and only if
* {@code y.equals(x)} returns {@code true}.
* <li>It is <i>transitive</i>: for any non-null reference values
* {@code x}, {@code y}, and {@code z}, if
* {@code x.equals(y)} returns {@code true} and
* {@code y.equals(z)} returns {@code true}, then
* {@code x.equals(z)} should return {@code true}.
* <li>It is <i>consistent</i>: for any non-null reference values
* {@code x} and {@code y}, multiple invocations of
* {@code x.equals(y)} consistently return {@code true}
* or consistently return {@code false}, provided no
* information used in {@code equals} comparisons on the
* objects is modified.
* <li>For any non-null reference value {@code x},
* {@code x.equals(null)} should return {@code false}.
* </ul>
* <p>
* The {@code equals} method for class {@code Object} implements
* the most discriminating possible equivalence relation on objects;
* that is, for any non-null reference values {@code x} and
* {@code y}, this method returns {@code true} if and only
* if {@code x} and {@code y} refer to the same object
* ({@code x == y} has the value {@code true}).
* <p>
* Note that it is generally necessary to override the {@code hashCode}
* method whenever this method is overridden, so as to maintain the
* general contract for the {@code hashCode} method, which states
* that equal objects must have equal hash codes.
*
* @param obj the reference object with which to compare.
* @return {@code true} if this object is the same as the obj
* argument; {@code false} otherwise.
* @see #hashCode()
* @see java.util.HashMap
*/

直接比較兩個對象是否是同一個對象code

下面是重寫equals方法須要知足的規則

  自反性:對於任何非空引用 x,x.equals(x) 應該返回 true

  對稱性:對於任何引用 x 和 y,當且僅當 y.equals(x) 返回 truex.equals(y) 也應該返回 true

  傳遞性:對於任何引用 x、y 和 z,若是 x.equals(y)返回 truey.equals(z) 也應返回一樣的結果

  一致性:若是 x 和 y 引用的對象沒有發生變化,反覆調用 x.equals(y) 應該返回一樣的結果

  對於任意非空引用 x,x.equals(null) 應該返回 false

對於非空引用類型的變量,(在沒有重寫equals和hashCode的狀況下)若是他們指向的在內存空間是同一個地址,那麼他們就相等。重寫equals方法最好也重寫equals方法,主要的目的是保持和hashcode的關係(相同的對象必須擁有相同的hash值)。

重寫了equals方法的幾個包裝類:File String Date 8個包裝類,他們比較的都是類型及內容而不考慮引用是否是同一個對象,

如:

  String的equals方法

public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
先比較是否是同一個對象,而後再比較長度,最後比較值,若是都知足相等,那麼他們纔是相等的

實例

public static void main(String[] args) {
String str1 = "BB";
String str2 = str1;
String str3 = new String("BB");
String str4 = new String("BB");

System.out.println(str1==str2);
System.out.println(str2==str3);
System.out.println(str3==str4);

System.out.println(str1.equals(str3));
System.out.println(str3.equals(str4));
}
結果:
  true
  false
  false
  true
  true
如今來試試自定義的類
  
public class Person {
Integer id;
String name;
}
這時我並無重寫equals方法
public static void main(String[] args) {
Person person1 = new Person(1, "小明");
Person person2 = new Person(1, "小明");

System.out.println(person1 == person2);
System.out.println(person1.equals(person2));
}
結果:
  false
  false
當我重寫父類的equals方法後
再打印一次
  false
  true
怎麼重寫equals和hashCode呢

  大部分流行的幾款ide都帶有這個功能,固然也能夠本身寫,可是我就偷懶直接生成了

@Overridepublic boolean equals(Object o) {    if (this == o) {        return true;    }    if (o == null || getClass() != o.getClass()) {        return false;    }    Person person = (Person) o;    if (id != null ? !id.equals(person.id) : person.id != null) {        return false;    }    return name != null ? name.equals(person.name) : person.name == null;}@Overridepublic int hashCode() {    int result = id != null ? id.hashCode() : 0;    result = 31 * result + (name != null ? name.hashCode() : 0);    return result;}其中hashCode的返回值是自定義的,好比你能夠將31改成32Tips:瞭解過JVM後對這兩個方法的理解會更透徹一點,結合源碼食用
相關文章
相關標籤/搜索