无法理解链接

时间:2016-04-29 17:17:38

标签: java data-structures hashtable chaining

我正在研究哈希表,并通过其实现来链接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检查,但最初nullkey没有任何value分配。那么检查怎么办呢?声明null后会private LinkedList<Entry<K, V>>[] table被分配吗?

// 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;
    }

}

2 个答案:

答案 0 :(得分:1)

我认为您无法正确显示哈希表。哈希表有两个同样好的简单实现。

方法1使用链表:链表的数组(实际上是Vector)。

给定“键”,您可以获得该键的哈希值(*)。您将该哈希值的剩余部分相对于向量的当前大小,我们称之为“x”。然后,您按顺序搜索向量[x]指向的链接列表,以便与您的密钥匹配。

(*)您希望哈希值分布合理。这样做有复杂的算法。让我们希望你的JVM实现HashCode做得很好。

方法2避免链接列表:您创建一个Vector并计算Vector中的索引(如上所述)。然后你看看Vector.get(x)。如果这是您想要的密钥,则返回相应的值。我们假设不是。然后你看看Vector.get(x + 1),Vector.get(x + 2)等。最后,将发生以下三件事之一:

a)您找到了您要找的钥匙。然后返回相应的值。 b)你找到一个空条目(key == null)。返回null或您选择的任何值意味着“这不是您正在寻找的机器人”。 c)您已检查过Vector中的每个条目。再次,返回null或其他。

检查(c)是一种预防措施,因此如果哈希表恰好已满,则不会永远循环。如果哈希表即将填满(您可以保留已使用的条目数),则应重新分配更大的哈希表。在IDeally,你想让哈希表保持足够稀疏,以至于你永远不会在 near 附近搜索整个表:这会破坏哈希表的整个目的 - 你可以用比线性更少的搜索它时间,理想情况是按顺序1(即,比较的数量是&lt; =小的常数)。我建议您分配一个至少是您希望放入其中的条目数的10倍的Vector。

在您的问题中使用“链接”一词表明您想要实现第二种类型的哈希表。

顺便说一句,你不应该使用10作为哈希表的大小。大小应该是素数。

希望这有帮助。

答案 1 :(得分:1)

  

假设表大小为10且key.hashCode()为124,因此索引为4.对于每个循环表[index]从表[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();
     }
 }