2016-04-29 2 views
0

Я изучаю хэш-таблицу с привязкой в ​​java по ее реализации. Проблема в том, что метод get(). Значение индекса определяется с помощью key.hashCode() % table.length. Предположим, что размер таблицы равен 10, а key.hashCode() - 124, поэтому индекс найден как 4. In для каждого цикла table[index] запускается с table[4], индекс AFAIK увеличивается один за другим 4,5,6,7... so on. Но как насчет индексов 0,1,2,3? Они были проверены? (Я думаю, нет) Нет ли возможности, что появление ключа на одном из индексов? (Я думаю да). Другой вопрос, который есть null проверяет, но изначально нет никакого null назначение для key и 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; 
    } 

} 
+0

Для хэш-таблицы _chained_ индекс никогда не увеличивается; нет никакой возможности записи, происходящей в любом другом ведре, чем 'table [4]'. –

ответ

1

Предположит, что размер таблицы 10 и key.hashCode() составляет 124 так, индекс найден, как 4. В таблице для каждой петли [индекс] запускается из таблицы [4]

Correct ,

Есть нулевые проверки, но изначально нет никакого нулевого назначения для ключа и значения. Итак, как можно проверить работу?

Когда инициализируется массив объектов, все значения установлены на 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 использует связанные списки: массив (ну, вектор, фактически) связанных списков.

Учитывая «ключ», вы получаете значение хэша для этого ключа (*). Вы берете оставшуюся часть этого хэш-значения относительно текущего размера вектора, назовем это «x». Затем вы последовательно просматриваете связанный список, который вектор [x] указывает на совпадение с вашим ключом.

(*) Вы надеетесь, что значения хэша будут достаточно хорошо распределены. Для этого существуют сложные алгоритмы. Будем надеяться, что ваша реализация JVM HashCode справится с этим.

Способ 2 исключает связанные списки: вы создаете вектор и вычисляете индекс в вектор (как указано выше). Затем вы смотрите на Vector.get (x). Если это тот ключ, который вы хотите, ваше возвращение соответствует соответствующему значению. Предположим, что это не так. Затем вы увидите Vector.get (x + 1), Vector.get (x + 2) и т. Д. В конце концов произойдет одно из следующих трех событий:

a) Вы найдете ключ, который вы ищете. Затем вы возвращаете соответствующее значение. b) вы найдете пустую запись (ключ == null). Возвращайте значение null или любое другое значение, которое вы выбрали для обозначения «это не тот дроид, который вы ищете». c) вы изучили каждую запись в векторе. Опять же, возвращаем нуль или что-то еще.

Проверка на (c) является предосторожностью, так что, если Хэш-таблица окажется заполненной, вы не зацикливаетесь навсегда. Если хэш-таблица будет заполнена (вы можете сохранить количество использованных записей), вы должны перераспределить большую хеш-таблицу. Иными словами, вы хотите, чтобы хэш-таблица была достаточно разреженной, чтобы вы нигде не попали около, просматривая всю таблицу: это уничтожает всю цель хэш-таблицы - вы можете искать ее в гораздо меньшем, чем линейное время, в идеальном порядке 1 (то есть число сравнений равно < = небольшая константа). Я бы предположил, что вы выделили вектор, который, по крайней мере, на 10 раз больше количества записей, которые вы ожидаете вставить в него.

Использование слова «цепочка» в ваших вопросах подсказывает мне, что вы хотите реализовать второй тип хеш-таблицы.

Btw, вы никогда не должны использовать 10 в качестве размера хеш-таблицы. Размер должен быть простым числом.

Надеюсь, это поможет.

+0

Ключевым моментом, который делает работу хеш-таблицы, является то, что он должен согласиться с методом equals: скажем, вы переопределяете метод equals в своем классе, вы должны переопределить hashcode и реализовать его таким образом, чтобы, если два экземпляра вернули true для equals, они должны возвратите то же значение хэш-кода. Обратное не обязательно верно: два экземпляра с одинаковым значением хэш-кода могут быть не равными. Это приводит к тому, как работает хэш-карта: сначала сортируйте по hashcode, затем при столкновении используйте метод equals. – kuporific