2017-03-09 135 views
1
import java.util.HashMap; 
import java.util.Map.Entry; 
public class TestString { 
    public static void main(String[] args) { 
     System.gc(); 

     String str = "deepak"; 
     int length = str.length(); 
     System.out.println("str " + str + " length " + length); 

     HashMap<Character,Integer> map = new HashMap<Character,Integer>(); 
     for(int i=0; i<length; i++){ 
      char ch = str.charAt(i); 
      if(map.containsKey(ch)){ 
       Integer val = map.get(ch); 
       map.put(ch, val+1); 
      }else{ 
       map.put(ch, 1); 
      } 
     } 

     for (Entry<Character, Integer> entry : map.entrySet()) 
     { 
      int hashCode = entry.hashCode(); 
      char key = entry.getKey(); 
      // int hash = hash(); 
      System.out.println("hashcode " + hashCode + " hashcode of key>> " + entry.getKey().hashCode() + " key : " + key); 
     } 
     System.out.println(">>> " + map); 
    } 
} 

輸出:
STR迪帕克長度6爪哇 - 哈希映射retreival序列

的密鑰哈希碼113哈希碼>> 112鍵:P

哈希碼96的密鑰哈希碼>> 97 key:a

hashcode密鑰的哈希碼>> 100 key:d

哈希碼103哈希碼密鑰>> 101鍵:電子

哈希碼106哈希碼的密鑰>> 107關鍵字:k

>>> {p = 1時,A = 1,d = 1,E = 2,K = 1}

誰能幫我瞭解從程序和輸出兩件事情:通過地圖對象印刷

  1. 的數據,如何決定在內部序列? 例如。它是打印序列p,a,d,e,k。

  2. entry.hashcode()和entry.key()。hashcode()有什麼區別? 請參考輸出解釋差異。

+0

您的條目的內部順序不受HashMap的保證。他們可以在任何其他地方。看看[這裏](http://stackoverflow.com/questions/683518/java-class-that-implements-map-and-keeps-insertion-order)。 – GAlexMES

+0

區別在[Java API](https://docs.oracle.com/javase/7/docs/api/java/util/Map.Entry.html#hashCode()) – GAlexMES

+0

中有說明程序開始處的'System.gc()'? – Holger

回答

0
  1. 據我所知,一個HashMap不garantueing的Entrys或鍵的順序。如果你想讓它們排序,你將需要一個TreeMap或者鏈接地圖。 See here.。 HashMap根本沒有順序。

  2. 我已經評論你鏈接到Java API,這說明它很好。但我會解釋reffering到您的輸出:

哈希碼113哈希碼鍵>> 112鍵:P

這意味着:入口的hashCode是113你的關鍵的哈希碼是112.你不打印你的Entry的HashCode,它是1.條目的HashCode是用Key的HashCode和Value的HashCode計算的。那些HashCodes將被着色。

+0

我同意沒有HashMap保證的順序,但我已經多次嘗試多個字符串數據,但它總是以相同的順序響應各個String數據,所以它意味着它有一些內部模式來打印輸出。 –

+0

不,它沒有。如果你在不同的機器或不同的操作系統,不同的處理器架構上嘗試相同的東西,或者只是經常嘗試。然後會有不同的順序。 – GAlexMES

0

迭代次序是未指定的,從技術上講,它是關鍵密碼,實際地圖實現,地圖容量以及某些情況下特定地圖實例的歷史記錄的函數。

您可以輕鬆地在我的環境改變你的代碼

String str = "deepak"; 
int length = str.length(); 
System.out.println("str " + str + " length " + length); 

HashMap<Character,Integer> map = new HashMap<>(100); 
for(int i=0; i<length; i++) { 
    char ch = str.charAt(i); 
    map.merge(ch, 1, Integer::sum); 
} 

for(Entry<Character, Integer> entry: map.entrySet()) { 
    int hashCode = entry.hashCode(); 
    Character key = entry.getKey(); 
    System.out.printf("hashcode %3d, hashcode of key %3d, key : %s%n", 
         hashCode, key.hashCode(), key); 
} 
map = new HashMap<>(map); 
System.out.println(">>> " + map); 

顯示依賴於地圖的能力,它打印

str deepak length 6 
hashcode 96, hashcode of key 97, key : a 
hashcode 101, hashcode of key 100, key : d 
hashcode 103, hashcode of key 101, key : e 
hashcode 106, hashcode of key 107, key : k 
hashcode 113, hashcode of key 112, key : p 
>>> {p=1, a=1, k=1, d=1, e=2} 

顯示具有不同容量的地圖如何表現出不同的迭代訂單。


的關鍵和Map.Entry實例是不同的對象,因此,這並不奇怪,他們有不同的散列碼。的Map.Entry哈希碼是well specified

映射項e的哈希碼被定義爲:

(e.getKey()==null ? 0 : e.getKey().hashCode())^
(e.getValue()==null ? 0 : e.getValue().hashCode()) 

這確保e1.equals(e2)意味着當需要e1.hashCode()==e2.hashCode()對於任何兩個條目e1e2,由總合同Object.hashCode

這符合Map意見的預期行爲。如果你有兩個Map S,ab,然後當且僅當這兩個地圖都有相同的鍵

  • a.keySet().equals(b.keySet())將是真實的。
  • a.entrySet().equals(b.entrySet())將爲真當且僅當兩者的地圖具有相同的鍵每個鍵映射到用於每個映射爲相同的值,換言之,這兩個圖是相等的。

    這甚至specified in Map.equals

    返回true如果給定對象也是一個映射並且兩個映射表示相同的映射。更正式地說,如果m1.entrySet().equals(m2.entrySet()),則兩張圖m1m2表示相同的映射。

  • 由於鍵和條目是不同的東西,a.keySet().equals(a.entrySet())只能是真實的,如果a是空的。

+0

當然!爲什麼不在這麼多標籤上評論最不尊敬的用戶呢?這是一個很好的(和詳細的!)答案...一加 – Eugene

0

這是一個有點不清楚你是想在這裏證明或問什麼,但如何對一個非常簡單的例子:

HashMap<String, Integer> map = new HashMap<>(); 
    map.put("HZV", 1); 
    map.put("CJX", 2); 

    System.out.println(map); // {CJX=2, HZV=1} 

    for (int i = 0; i < 16; ++i) { 
     map.put("" + i, i); 
    } 

    for (int i = 0; i < 16; ++i) { 
     map.remove("" + i); 
    } 

    System.out.println(map); // {HZV=1, CJX=2} 

請注意,在該示例同時打印上面存儲相同內容,地圖中的條目是相同的;唯一的區別是它們的排列順序是,排列順序不同

這是因爲內部地圖存儲中buckets的數量已增加,並且因爲條目爲re-hashed而且它們已移至其他存儲桶。從表中檢索條目時,很明顯there is no guaranteed sequence

這裏的其他答案都指出,Map的說明也是這樣說的。

這甚至更好的是出現在JDK-9一成不變的地圖:

Map<String, Integer> map2 = Map.of("1", 1, "2", 2, "3", 3); 
System.out.println(map2); 

這個map2的輸出將從每次運行變化!這對於像這樣的輸出(在不同的虛擬機上運行的兩倍)完全合法:

{1=1, 3=3, 2=2} 

{1=1, 2=2, 3=3} 

這正是因爲地圖沒有訂單,所以他們實施的地圖內隨機圖案。

編輯

的解釋一點點。

我會嘗試,但這是一個整體的大課題。所以一個HashMap將使用buckets來存儲它的鍵/值對。根據大小,桶實際上是一個Node s(紅/黑TreeNode或LinkedNode)的數組。條目使用鍵和模的hashCode轉到某個存儲桶。那麼不是直接模塊,而是使用& operator(兩個散列與模數的搜索能力)的技巧。重新調整大小(實際上是內部數組上的加倍 - 因此觸發重新哈希)是在某些條件下完成的,其中最顯着的是當達到load_factor時(但不是唯一的)。因此,如果聲明:new HashMap(16),則加載因子爲0.75,因此當您添加13條目時,內部數組將重新調整爲32個條目。這也會觸發密鑰的重新哈希(在引擎蓋下還有一點是在選擇桶時考慮到的 - 所以可能會移動 - 就像我提供的示例中那樣)。這不依賴於JVM,但依賴於實現。 這是jdk-8和jdk-9當時的行爲。但無論哪種方式你不應該在乎,而是依賴於地圖提供的一般合同。

+0

感謝您詳細解釋。我知道在HashMap調整大小發生75%,但我不知道它是如何實際工作的某些特定的程序或機器。你能解釋一下hashmap在內存中調整大小的方式以及它實際上觸發調整大小的方法嗎?調整大小對於程序來說是特定的,還是依賴於JVM? –

+0

@SomeshGupta我做了一個編輯,添加*一些*細節。但通常這是一個需要分離的重大課題;一次不容易理解。我試圖解決這個問題,前一段時間,這裏是我發佈我的發現的地方.. https://www.slideshare.net/secret/8k8EP71cNydeib – Eugene

+0

謝謝@Eugene。但是當你說這一行時 - 「重新調整大小(實際上是內部數組的雙倍 - 因此觸發重新哈希)是在某些條件下完成的」,所以你的意思是內部數組List(Map.Entry),或者你在說話關於別的東西? –