簡體   English   中英

Java中hashCode()是怎么計算的

[英]How is hashCode() calculated in Java

java中hashCode()方法返回什么值?

我讀到它是 object 的 memory 引用... new Integer(1)的 hash 值為 1; String("a")的 hash 值為 97。

我很困惑:它是 ASCII 還是什么類型的值?

hashCode()返回的值絕不保證是對象的內存地址。 我不確定Object類中的實現,但請記住,大多數類將覆蓋hashCode()以便語義上等效(但不是同一個實例)的兩個實例將散列到相同的值。 如果這些類可能在另一個數據結構中使用,例如 Set,依賴hashCodeequals一致,這一點尤其重要。

無論如何都不存在唯一標識對象實例的hashCode() 如果您想要基於底層指針的哈希碼(例如在 Sun 的實現中),請使用System.identityHashCode() - 這將委托給默認的hashCode方法,無論它是否已被覆蓋。

盡管如此,即使System.identityHashCode()也可以為多個對象返回相同的哈希值。 請參閱注釋以獲取解釋,但這里有一個示例程序,它不斷生成對象,直到找到兩個具有相同System.identityHashCode() 當我運行它時,在向映射添加大約 86,000 個 Long 包裝器對象(以及鍵的 Integer 包裝器)后,它會迅速找到兩個匹配的System.identityHashCode()

public static void main(String[] args) {
    Map<Integer,Long> map = new HashMap<>();
    Random generator = new Random();
    Collection<Integer> counts = new LinkedList<>();

    Long object = generator.nextLong();
    // We use the identityHashCode as the key into the map
    // This makes it easier to check if any other objects
    // have the same key.
    int hash = System.identityHashCode(object);
    while (!map.containsKey(hash)) {
        map.put(hash, object);
        object = generator.nextLong();
        hash = System.identityHashCode(object);
    }
    System.out.println("Identical maps for size:  " + map.size());
    System.out.println("First object value: " + object);
    System.out.println("Second object value: " + map.get(hash));
    System.out.println("First object identityHash:  " + System.identityHashCode(object));
    System.out.println("Second object identityHash: " + System.identityHashCode(map.get(hash)));
}

示例輸出:

Identical maps for size:  105822
First object value: 7446391633043190962
Second object value: -8143651927768852586
First object identityHash:  2134400190
Second object identityHash: 2134400190

哈希碼是一個整數值,表示調用它的對象的狀態。 這就是為什么設置為 1 的Integer將返回“1” Integer's哈希碼,因為Integer's哈希碼和它的值是相同的。 一個字符的哈希碼等於它的 ASCII 字符碼。 如果您編寫自定義類型,則您有責任創建一個良好的hashCode實現,它最能代表當前實例的狀態。

如果你想知道它們是如何實現的,我建議你閱讀源代碼。 如果您使用的是 IDE,您只需在您感興趣的方法上添加 +,然后查看該方法是如何實現的。 如果你不能這樣做,你可以谷歌尋找來源。

例如,Integer.hashCode() 實現為

   public int hashCode() {
       return value;
   }

和 String.hashCode()

   public int hashCode() {
       int h = hash;
       if (h == 0) {
           int off = offset;
           char val[] = value;
           int len = count;

           for (int i = 0; i < len; i++) {
               h = 31*h + val[off++];
           }
           hash = h;
       }
       return h;
   }

hashCode()方法通常用於識別對象。 我認為Object實現返回Object的指針(不是真正的指針,而是唯一的 id 或類似的東西)。 但是大多數類會覆蓋該方法。 String類。 兩個 String 對象沒有相同的指針但它們是相等的:

new String("a").hashCode() == new String("a").hashCode()

我認為hashCode()最常見的用途是在HashtableHashSet等中。

Java API 對象 hashCode()

編輯:(由於最近的投票反對,並且基於我閱讀的有關 JVM 參數的文章)

使用 JVM 參數-XX:hashCode您可以更改 hashCode 的計算方式(請參閱 Java 專家通訊的第 222期)。

HashCode==0:簡單地返回與對象在內存中的位置無關的隨機數。 據我所知,種子的全局讀寫對於具有大量處理器的系統來說並不是最佳的。

HashCode==1:計算哈希碼值,不確定它們從什么值開始,但似乎相當高。

HashCode==2:始終返回與 1 完全相同的身份哈希碼。這可用於測試依賴於對象身份的代碼。 JavaChampionTest 在上例中返回 Kirk 的 URL 的原因是所有對象都返回相同的哈希碼。

HashCode==3:從零開始計算哈希碼值。 它看起來不是線程安全的,因此多個線程可以生成具有相同哈希碼的對象。

HashCode==4:這似乎與創建對象的內存位置有某種關系。

HashCode>=5:這是 Java 8 的默認算法,並且具有每線程種子。 它使用 Marsaglia 的 xor-shift 方案來產生偽隨機數。

我讀到它是一個對象的內存引用..

不是。大約 14 年前, Object.hashCode()用於返回內存地址。 不是從那以后。

什么類型的值

它是什么完全取決於您所談論的類以及它是否覆蓋了`Object.hashCode()。

來自 OpenJDK 源代碼 (JDK8):

使用默認值 5 生成哈希碼:

product(intx, hashCode, 5,                                                
      "(Unstable) select hashCode generation algorithm")       

一些常量數據和隨機生成的數字,每個線程啟動一個種子:

// thread-specific hashCode stream generator state - Marsaglia shift-xor form
  _hashStateX = os::random() ;
  _hashStateY = 842502087 ;
  _hashStateZ = 0x8767 ;    // (int)(3579807591LL & 0xffff) ;
  _hashStateW = 273326509 ;

然后,此函數創建 hashCode(如上指定的默認為 5):

static inline intptr_t get_next_hash(Thread * Self, oop obj) {
  intptr_t value = 0 ;
  if (hashCode == 0) {
     // This form uses an unguarded global Park-Miller RNG,
     // so it's possible for two threads to race and generate the same RNG.
     // On MP system we'll have lots of RW access to a global, so the
     // mechanism induces lots of coherency traffic.
     value = os::random() ;
  } else
  if (hashCode == 1) {
     // This variation has the property of being stable (idempotent)
     // between STW operations.  This can be useful in some of the 1-0
     // synchronization schemes.
     intptr_t addrBits = cast_from_oop<intptr_t>(obj) >> 3 ;
     value = addrBits ^ (addrBits >> 5) ^ GVars.stwRandom ;
  } else
  if (hashCode == 2) {
     value = 1 ;            // for sensitivity testing
  } else
  if (hashCode == 3) {
     value = ++GVars.hcSequence ;
  } else
  if (hashCode == 4) {
     value = cast_from_oop<intptr_t>(obj) ;
  } else {
     // Marsaglia's xor-shift scheme with thread-specific state
     // This is probably the best overall implementation -- we'll
     // likely make this the default in future releases.
     unsigned t = Self->_hashStateX ;
     t ^= (t << 11) ;
     Self->_hashStateX = Self->_hashStateY ;
     Self->_hashStateY = Self->_hashStateZ ;
     Self->_hashStateZ = Self->_hashStateW ;
     unsigned v = Self->_hashStateW ;
     v = (v ^ (v >> 19)) ^ (t ^ (t >> 8)) ;
     Self->_hashStateW = v ;
     value = v ;
  }

  value &= markOopDesc::hash_mask;
  if (value == 0) value = 0xBAD ;
  assert (value != markOopDesc::no_hash, "invariant") ;
  TEVENT (hashCode: GENERATE) ;
  return value;
}

所以我們可以看到至少在 JDK8 中默認設置為隨機線程特定。

定義:String hashCode() 方法以整數形式返回字符串的哈希碼值。

語法: public int hashCode()

哈希碼使用以下公式計算

s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]

where:

s is ith character in the string
n is length of the string
^ is exponential operand

示例:例如,如果您想計算字符串“abc”的哈希碼,那么我們有以下詳細信息

s[] = {'a', 'b', 'c'}
n = 3

因此哈希碼值將計算為:

s[0]*31^(2) + s[1]*31^1 + s[2]
= a*31^2 + b*31^1 + c*31^0
= (ASCII value of a = 97, b = 98 and c = 99)
= 97*961 + 98*31 + 99 
= 93217 + 3038 + 99
= 96354

所以 ' abc ' 的哈希碼值為96354

Object.hashCode(),如果內存服務正確(檢查 java.lang.Object 的 JavaDoc),是依賴於實現的,並且會根據對象而改變(Sun JVM 從對對象的引用的值中獲得值)。

請注意,如果您正在實現任何非平凡對象,並希望將它們正確存儲在 HashMap 或 HashSet 中,則必須覆蓋 hashCode() 和 equals()。 hashCode() 可以做任何你喜歡的事情(它完全合法,但讓它返回 1 是次優的。),但至關重要的是,如果你的 equals() 方法返回 true,那么 hashCode() 為兩個對象返回的值是相等的。

對 hashCode() 和 equals() 的混淆和缺乏理解是錯誤的重要來源。 確保您完全熟悉 Object.hashCode() 和 Object.equals() 的 JavaDocs,我保證所花費的時間會物有所值。

來自 Javadoc:

盡可能實用,類 Object 定義的 hashCode 方法確實為不同的對象返回不同的整數。 (這通常通過將對象的內部地址轉換為整數來實現,但 Java™ 編程語言不需要這種實現技術。)

https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#hashCode--

我很驚訝沒有人提到這一點,但是盡管對於任何非Object類來說這是顯而易見的,但您的第一個動作應該是閱讀許多類的源代碼.hashcode()只是從Object擴展而來,在這種情況下,有幾個不同的有趣的東西可能會根據您的 JVM 實現而發生。 Object.hashcode()調用System.identityHashcode(object)

事實上,在內存中使用對象地址是古老的歷史,但許多人沒有意識到他們可以控制這種行為以及如何通過 jvm 參數-XX:hashCode=N計算Object.hashcode() ,其中 N 可以是 [0-5] 中的數字。 ..

0 – Park-Miller RNG (default, blocking)
1 – f(address, global_statement)
2 – constant 1
3 – serial counter
4 – object address
5 – Thread-local Xorshift

根據應用程序,當調用.hashcode()時,您可能會看到意外的性能.hashcode() ,當這種情況發生時,您可能正在使用共享全局狀態和/或塊的算法之一。

根據“將 object 的內部地址轉換為整數”的 javaDoc。 因此很明顯 hashCode() 方法不會按原樣返回 object 的內部地址。 下面提供了鏈接。 https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#hashCode--

要清除它,請參見以下示例代碼:

public class HashCodeDemo
    {
    public static void main(String[] args)
        {
        final int CAPACITY_OF_MAP = 10000000;

        /**
         * hashCode as key, and Object as value
         */
        java.util.HashMap<Integer, Object> hm1 = new java.util.HashMap<Integer, Object>(CAPACITY_OF_MAP);
        int noOfDistinceObject = 0;
        Object obj = null;
        for(int i = 0; i < CAPACITY_OF_MAP; i++)
            {
            obj = new Object();
            hm1.put(obj.hashCode(), new Object());
            }
        System.out.println("hm1.size() = "+hm1.size());

        /**
         * hashCode as key, and Object as value
         */
        java.util.HashMap<Integer, Object> hm2 = new java.util.HashMap<Integer, Object>(CAPACITY_OF_MAP);
        for(int i = 0; i < CAPACITY_OF_MAP; i++)
            {
            obj = new Object();
            /**
             * Each Object has unique memory location , 
             * and if Object's hashCode is memory location then hashCode of Object is also unique
             * then no object can put into hm2.
             * 
             * If obj's hashCode is doesn't exists in hm1 then increment noOfDistinceObject , else add obj into hm2.
             */
            if(hm1.get(obj.hashCode()) == null)
                {
                noOfDistinceObject++;
                }
            else
                {
                hm2.put(obj.hashCode(), new Object());
                }
            }

        System.out.println("hm2.size() = "+hm2.size());
        System.out.println("noOfDistinceObject = "+noOfDistinceObject);
        }
    }

每個 Object 都有唯一的 memory 位置,如果 Object 的 hashCode 方法返回 memory 位置,那么 Object 的 hashCode 也是唯一的,但是如果我們運行上面的示例代碼,那么一些對象具有相同的 hashcode 值,一些具有唯一的 hashcode 值。

所以我們可以說來自 Object class 的 hashCode 方法不返回 memory 位置。

hashCode()方法在Java中返回什么值?

我讀到它是一個對象的內存引用... new Integer(1)的哈希值是1; String("a")的哈希值為97。

我很困惑:是ASCII還是什么類型的值?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM