2016-04-29 44 views
0

我正在學習哈希表與Java中的鏈接通過它的實現。麻煩大約是get()方法。指數值由key.hashCode() % table.length確定。假設表大小爲10,並且key.hashCode()爲124,因此索引被找到爲4。在每個循環table[index]table[4]開始,AFAIK索引逐一遞增4,5,6,7... so on。但是指數0,1,2,3呢?他們被檢查了嗎? (我認爲不是)在其中一個指數上出現關鍵的可能性不存在嗎? (我想是的)。另一個問題是有null檢查,但最初沒有任何null分配keyvalue。那麼檢查工作如何?一旦private LinkedList<Entry<K, V>>[] table被宣佈,null是否被分配?理解哈希表與鏈接的實現的問題

// Data Structures: Abstraction and Design Using Java, Koffman, Wolfgang 

package KW.CH07; 

import java.util.AbstractMap; 
import java.util.Iterator; 
import java.util.LinkedList; 
import java.util.List; 
import java.util.Map; 
import java.util.StringJoiner; 

/** 
* Hash table implementation using chaining. 
* @param <K> The key type 
* @param <V> The value type 
* @author Koffman and Wolfgang 
**/ 
public class HashtableChain<K, V> 
// Insert solution to programming project 7, chapter -1 here 
    implements KWHashMap<K, V> { 

    /** The table */ 
    private LinkedList<Entry<K, V>>[] table; 
    /** The number of keys */ 
    private int numKeys; 
    /** The capacity */ 
    private static final int CAPACITY = 101; 
    /** The maximum load factor */ 
    private static final double LOAD_THRESHOLD = 3.0; 

    // Note this is equivalent to java.util.AbstractMap.SimpleEntry 
    /** Contains key-value pairs for a hash table. 
     @param <K> the key type 
     @param <V> the value type 
    */ 
    public static class Entry<K, V> 
// Insert solution to programming project 6, chapter -1 here 
    { 

     /** The key */ 
     private final K key; 
     /** The value */ 
     private V value; 

     /** 
     * Creates a new key-value pair. 
     * @param key The key 
     * @param value The value 
     */ 
     public Entry(K key, V value) { 
      this.key = key; 
      this.value = value; 
     } 

     /** 
     * Retrieves the key. 
     * @return The key 
     */ 
     @Override 
     public K getKey() { 
      return key; 
     } 

     /** 
     * Retrieves the value. 
     * @return The value 
     */ 
     @Override 
     public V getValue() { 
      return value; 
     } 

     /** 
     * Sets the value. 
     * @param val The new value 
     * @return The old value 
     */ 
     @Override 
     public V setValue(V val) { 
      V oldVal = value; 
      value = val; 
      return oldVal; 
     } 

// Insert solution to programming exercise 3, section 4, chapter 7 here 
    } 

    // Constructor 
    public HashtableChain() { 
     table = new LinkedList[CAPACITY]; 
    } 

    // Constructor for test purposes 
    HashtableChain(int capacity) { 
     table = new LinkedList[capacity]; 
    } 

    /** 
    * Method get for class HashtableChain. 
    * @param key The key being sought 
    * @return The value associated with this key if found; 
    *   otherwise, null 
    */ 
    @Override 
    public V get(Object key) { 
     int index = key.hashCode() % table.length; 
     if (index < 0) { 
      index += table.length; 
     } 
     if (table[index] == null) { 
      return null; // key is not in the table. 
     } 
     // Search the list at table[index] to find the key. 
     for (Entry<K, V> nextItem : table[index]) { 
      if (nextItem.getKey().equals(key)) { 
       return nextItem.getValue(); 
      } 
     } 

     // assert: key is not in the table. 
     return null; 
    } 

    /** 
    * Method put for class HashtableChain. 
    * @post This key-value pair is inserted in the 
    *  table and numKeys is incremented. If the key is already 
    *  in the table, its value is changed to the argument 
    *  value and numKeys is not changed. 
    * @param key The key of item being inserted 
    * @param value The value for this key 
    * @return The old value associated with this key if 
    *   found; otherwise, null 
    */ 
    @Override 
    public V put(K key, V value) { 
     int index = key.hashCode() % table.length; 
     if (index < 0) { 
      index += table.length; 
     } 
     if (table[index] == null) { 
      // Create a new linked list at table[index]. 
      table[index] = new LinkedList<>(); 
     } 

     // Search the list at table[index] to find the key. 
     for (Entry<K, V> nextItem : table[index]) { 
      // If the search is successful, replace the old value. 
      if (nextItem.getKey().equals(key)) { 
       // Replace value for this key. 
       V oldVal = nextItem.getValue(); 
       nextItem.setValue(value); 
       return oldVal; 
      } 
     } 

     // assert: key is not in the table, add new item. 
     table[index].addFirst(new Entry<>(key, value)); 
     numKeys++; 
     if (numKeys > (LOAD_THRESHOLD * table.length)) { 
      rehash(); 
     } 
     return null; 
    } 

    /** Returns true if empty 
     @return true if empty 
    */ 
    @Override 
    public boolean isEmpty() { 
     return numKeys == 0; 
    } 

} 
+0

對於_chained_哈希表,索引永遠不會增加;除了table [4]'之外,其他任何桶中都不可能出現該條目。 –

回答

1

假設表的大小是10和key.hashCode()是124,索引被發現爲4.在每個環路表[索引]從表[4]

正確開始。

有空檢查,但最初沒有任何關鍵和值的空分配。那麼檢查工作如何?

當一個對象數組被初始化時,所有的值都被設置爲null

索引正在逐個遞增4,5,6,7 ......等等。但是,指數0,1,2,3呢?他們被檢查了嗎? (我認爲不是)在其中一個指數上出現關鍵的可能性不存在嗎? (我想是的)。

看起來這裏有一些誤解。首先,認爲這樣的數據結構(與在已經被添加到它的數據):

table: 
[0] -> null 
[1] -> LinkedList -> item 1 -> item 2 -> item 3 
[2] -> LinkedList -> item 1 
[3] -> null 
[4] -> LinkedList -> item 1 -> item 2 
[5] -> LinkedList -> item 1 -> item 2 -> item 3 -> item 4 
[6] -> null 

另外重要的一點是,對於一個給定key哈希碼不應該改變,所以它總是會映射到表中的索引相同。

所以說我們叫get與價值誰的哈希碼將其映射爲3,那麼我們就知道它不是在表:

if (table[index] == null) { 
    return null; // key is not in the table. 
} 

如果另一個關鍵進來映射到1,現在我們需要迭代LinkedList:

// LinkedList<Entry<K, V>> list = table[index] 
for (Entry<K, V> nextItem : table[index]) { 
    // iterate over item 1, item 2, item 3 until we find one that is equal. 
    if (nextItem.getKey().equals(key)) { 
     return nextItem.getValue(); 
    } 
} 
1

我認爲你不是很正確地顯示你的散列表。哈希表有兩個同樣好的簡單實現。

方法1使用鏈接列表:鏈接列表的數組(實際上,Vector,實際上)。

給定一個「密鑰」,可以爲該密鑰(*)派生一個散列值。您將該散列值的其餘部分相對於矢量的當前大小,我們稱之爲「x」。然後,按順序搜索vector [x]指向的鏈接列表以與您的密鑰匹配。

(*)您希望散列值的分佈合理。這樣做有複雜的算法。讓我們希望你的JVM實現HashCode在這方面做得很好。方法2避免鏈接列表:你創建一個Vector並計算向量的索引(如上)。然後你看看Vector.get(x)。如果這是你想要的關鍵,你將返回相應的值。我們假設它不是。然後你看看Vector.get(x + 1),Vector.get(x + 2)等等。最終,以下三件事中的一件將會發生:

a)你找到你要找的鑰匙。然後你返回相應的值。 b)你找到一個空的條目(key == null)。返回null或你選擇的任何值意味着「這不是你正在尋找的機器人」。 c)你已經檢查了Vector中的每個條目。再次,返回null或其他。

檢查(c)是一項預防措施,因此如果哈希表恰好滿了,您將不會永久循環。如果哈希表即將滿(您可以保留已使用了多少條目的計數),則應重新分配更大的哈希表。實際上,您希望保持哈希表足夠稀疏,以至於您永遠無法獲得任何地方附近搜索整個表格:這會使哈希表的整個目的變得無用 - 您可以在比線性時間少得多的時間內搜索它,理想情況下按順序1(即比較的數量是< =一個小常量)。我建議你分配一個至少爲你希望放入的條目數量10倍的Vector。

在您的問題中使用「鏈接」這個詞向我建議您要實現第二種類型的散列表。

順便說一句,你不應該使用10作爲散列表的大小。大小應該是一個素數。

希望這會有所幫助。

+0

使哈希表工作的一個關鍵點是它需要與equals方法一致:假設你重寫了你的類的equals方法,你必須重寫哈希碼並實現它,這樣如果兩個實例返回true爲equals,它們應該返回相同的哈希碼值。反過來並不一定是正確的:具有相同哈希碼值的兩個實例可能不相等。這會影響哈希映射的工作方式:首先按哈希碼排序,然後在碰撞時使用equals方法。 – kuporific