【问题标题】:Linear Probing on Java HashTable implementationJava HashTable 实现的线性探测
【发布时间】:2013-02-11 04:47:59
【问题描述】:

所以我在这里有一个 HashTable 实现,我只使用 Arrays 编写了它,并且对代码有一点帮助。不幸的是,我不太理解有人在运行“get”或“put”方法时添加的其中一行。下面的while循环到底发生了什么?这是一种线性探测的方法正确吗?另外为什么循环检查它正在检查的条件?

具体来说,

int hash = hashThis(key);

    while(data[hash] != AVAILABLE && data[hash].key() != key) {

        hash = (hash + 1) % capacity;
    }

下面是整个 Java 类,以供完整参考。

public class Hashtable2 {

private Node[] data;
private int capacity;
private static final Node AVAILABLE = new Node("Available", null);

public Hashtable2(int capacity) {

    this.capacity = capacity; 
    data = new Node[capacity];
    for(int i = 0; i < data.length; i++) {

        data[i] = AVAILABLE;
    }
}

public int hashThis(String key) {

    return key.hashCode() % capacity; 
}

public Object get(String key) {

    int hash = hashThis(key);

    while(data[hash] != AVAILABLE && data[hash].key() != key) {

        hash = (hash + 1) % capacity;
    }
    return data[hash].element();
}

public void put(String key, Object element) {

    if(key != null) {
        int hash = hashThis(key);
        while(data[hash] != AVAILABLE && data[hash].key() != key) {

            hash = (hash + 1) % capacity;
        }

        data[hash] = new Node(key, element);

    }

}



public String toString(){

    String s="<";

    for (int i=0;i<this.capacity;i++)
    {
        s+=data[i]+", ";    

    }

    s+=">";

    return s;
    }

谢谢。

【问题讨论】:

    标签: java hashtable linear-probing


    【解决方案1】:

    我只是重写了部分代码并添加了 findHash 方法 - 尽量避免代码重复!

    private int findHash(String key) {
        int hash = hashThis(key);
    
        // search for the next available element or for the next matching key
        while(data[hash] != AVAILABLE && data[hash].key() != key) {
    
            hash = (hash + 1) % capacity;
        }
        return hash;
    }
    
    public Object get(String key) {
    
        return data[findHash(key)].element();
    }
    
    public void put(String key, Object element) {
    
        data[findHash(key)] = new Node(key, element); 
    }
    

    您要求的是 - 这个 findHash-loop 到底是什么?数据是用AVAILABLE 初始化的——意思是:数据(还)不包含任何实际数据。现在 - 当我们添加一个带有 put 的元素时 - 首先计算一个 hashValue,这只是 data 数组中放置数据的索引。现在 - 如果我们遇到该位置已被另一个具有相同哈希值但不同键的元素占据,我们尝试找到下一个AVAILABLE 位置。 get 方法的工作原理基本相同 - 如果检测到具有不同键的数据元素,则探测下一个元素,依此类推。 data 本身就是一个所谓的环形缓冲区。也就是说,它一直搜索到数组的末尾,然后在开始处再次搜索,从索引 0 开始。这是通过模 % 运算符完成的。

    好吗?

    【讨论】:

    • P.S.该实现缺少一些重要的方法:散列满时变大。目前,当您将元素放入已经完整的散列时,您将有一个无限循环。也没有删除可能。尝试为此编写一些单元测试。
    • 非常感谢!是的,我肯定会考虑添加这些内容,只是进入这些内容,但我很高兴学习!
    【解决方案2】:

    使用泛型和线性探测解决冲突的示例 Hashtable 实现。在实现过程中有一些假设,它们记录在上面的类和方法的 javadoc 中。

    此实现没有 Hashtable 的所有方法,如 keySet、putAll 等,但涵盖了最常用的方法,如 get、put、remove、size 等。

    get、put、remove查找索引有重复代码,可改进为有新的查找索引方法。

    class HashEntry<K, V> {
        private K key;
        private V value;
        public HashEntry(K key, V value) {
            this.key = key;
            this.value = value;
        }
        public void setKey(K key) { this.key = key; }
        public K getKey() { return this.key; }
    
        public void setValue(V value) { this.value = value; }
        public V getValue() { return this.value; }
    }
    
    /**
     * Hashtable implementation ...
     *   - with linear probing
     *   - without loadfactor & without rehash implementation.
     *   - throws exception when table is full
     *   - returns null when trying to remove non existent key
     *
     * @param <K>
     * @param <V>
     */
    public class Hashtable<K, V> {
    
        private final static int DEFAULT_CAPACITY = 16;
    
        private int count;
        private int capacity;
        private HashEntry<K, V>[] table;
    
        public Hashtable() {
            this(DEFAULT_CAPACITY);
        }
    
        public Hashtable(int capacity) {
            super();
            this.capacity = capacity;
            table = new HashEntry[capacity];
        }
    
        public boolean isEmpty() { return (count == 0); }
    
        public int size() { return count; }
    
        public void clear() { table = new HashEntry[this.capacity]; count = 0; }
    
        /**
         * Returns null if either probe count is higher than capacity else couldn't find the element.
         * 
         * @param key
         * @return
         */
        public V get(K key) {
            V value = null;
            int probeCount = 0;
            int hash = this.hashCode(key);
            while (table[hash] != null && !table[hash].getKey().equals(key) && probeCount <= this.capacity) {
                hash = (hash + 1) % this.capacity;
                probeCount++;
            }
            if (table[hash] != null && probeCount <= this.capacity) {
                value = table[hash].getValue();
            }
            return value; 
        }
    
        /**
         * Check on the no of probes done and terminate if probe count reaches to its capacity.
         * 
         * Throw Exception if table is full.
         * 
         * @param key
         * @param value
         * @return
         * @throws Exception
         */
        public V put(K key, V value) throws Exception {
            int probeCount = 0;
            int hash = this.hashCode(key);
            while (table[hash] != null && !table[hash].getKey().equals(key) && probeCount <= this.capacity) {
                hash = (hash + 1) % this.capacity;
                probeCount++;
            }
            if (probeCount <= this.capacity) {
                if (table[hash] != null) {
                    table[hash].setValue(value);                
                } else {
                    table[hash] = new HashEntry(key, value);
                    count++;
                }
                return table[hash].getValue();
            } else {
                throw new Exception("Table Full!!");
            }
        }
    
        /**
         * If key present then mark table[hash] = null & return value, else return null.  
         * 
         * @param key
         * @return
         */
        public V remove(K key) {
            V value = null;
            int probeCount = 0;
            int hash = this.hashCode(key);
            while (table[hash] != null && !table[hash].getKey().equals(key) && probeCount <= this.capacity) {
                hash = (hash + 1) % this.capacity;
                probeCount++;
            }
            if (table[hash] != null && probeCount <= this.capacity) {
                value = table[hash].getValue(); 
                table[hash] = null;
                count--;
            }
            return value;
        }
    
        public boolean contains(Object value) {
            return this.containsValue(value);
        }
    
        public boolean containsKey(Object key) {
            for (HashEntry<K, V> entry : table) {
                if (entry != null && entry.getKey().equals(key)) {
                    return true;
                }
            }
            return false;
        }
    
        public boolean containsValue(Object value) {
            for (HashEntry<K, V> entry : table) {
                if (entry != null && entry.getValue().equals(value)) {
                    return true;
                }
            }
            return false;
        }
    
        @Override
        public String toString() {
            StringBuilder data = new StringBuilder();
            data.append("{");
            for (HashEntry<K, V> entry : table) {
                if (entry != null) {
                    data.append(entry.getKey()).append("=").append(entry.getValue()).append(", ");
                }
            }
            if (data.toString().endsWith(", ")) {
                data.delete(data.length() - 2, data.length());
            }
            data.append("}");
            return data.toString();
        }
    
        private int hashCode(K key) { return (key.hashCode() % this.capacity); }
    
        public static void main(String[] args) throws Exception {
            Hashtable<Integer, String> table = new Hashtable<Integer, String>(2);
            table.put(1, "1");
            table.put(2, "2");
            System.out.println(table);
            table.put(1, "3");
            table.put(2, "4");
            System.out.println(table);
            table.remove(1);
            System.out.println(table);
            table.put(1, "1");
            System.out.println(table);
            System.out.println(table.get(1));
            System.out.println(table.get(3));
            // table is full so below line
            // will throw an exception
            table.put(3, "2");
        }
    }
    

    上述代码的示例运行。

    {2=2, 1=1}
    {2=4, 1=3}
    {2=4}
    {2=4, 1=1}
    1
    null
    Exception in thread "main" java.lang.Exception: Table Full!!
        at Hashtable.put(Hashtable.java:95)
        at Hashtable.main(Hashtable.java:177)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-01-28
      • 2020-04-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-27
      • 2013-03-25
      • 2015-08-21
      相关资源
      最近更新 更多