Ассоциативные списки и хеширование

advertisement
Ассоциативные списки
Поиск данных происходит не по индексу или положению объекта,
а по его ассоциативной связи:
public interface Map {
// Доступ к объектам по ключу
Object get(Object key);
Object put(Object key, Object value);
Object remove(Object key);
// Размер ассоциативного списка
int size();
boolean isEmpty();
// Проверка наличия ключей и значений
boolean containsKey(Object key);
boolean containsValue(Object value);
// Очистка списка
void clear();
}
Другие классы и интерфейсы: Dictionary, HashMap, Hashtable
Способы реализации ассоциативных списков
class MapContainer implements Map {
// Структура, содержащая ассоциативную связь
private static class Pair {
Object key, value;
Pair(Object key, Object value) {
this.key = key; this.value = value;
}
}
// Структура для поиска по ключу:
// список, упорядоченный массив, дерево,...
private SearchStruct container = new SearchStruct();
// Реализация операций:
Object get(Object key) {
// Поиск по ключу и возврат найденного значения
}
Object put(Object key, Object value) {
Pair newPair = new Pair(key, value);
// Вставка нового элемента с заданным ключом
}
}
Преимущества и недостатки различных структур данных
для реализации ассоциативных списков
Преимущества
Упорядоченный
массив



Список



Двоичное
дерево


Недостатки
Логарифмическая
скорость поиска
Простота слияния
Легкость итерации

Низкая скорость вставки
и удаления элементов
Удобство вставки и
удаления элементов
Простота слияния
Легкость итерации

Низкая скорость поиска
Логарифмическая
скорость поиска
Сравнительно быстрая
вставка и удаление
элементов


Сложность итерации
Слияние только
поэлементное
Хеширование
Идея: разделить все элементы на легко вычисляемые классы, и для каждого класса
хранить свой собственный структурный объект.
class MapContainer implements Map {
// Параметры хеширования:
final private static int HASH_MODULE = 128;
private static int hash(Object obj) {
return obj.hashCode() % HASH_MODULE;
}
// Структура, содержащая ассоциативную связь
private static class Pair { Object key, value; ... }
// массив структур: списков, упорядоченных массивов, деревьев,...
private SearchStruct containers[] = new SearchStruct[HASH_MODULE];
// Реализация операций:
Object get(Object key) {
// Поиск по ключу и возврат найденного значения
SearchStruct container = containers[hash(key)]; ...
}
Object put(Object key, Object value) {
Pair newPair = new Pair(key, value);
// Вставка нового элемента с заданным ключом в containers[hash(key)]
}
}
Преимущества и недостатки хеш-таблиц
Преимущества




Возможность использования
простых структур хранения
Быстрый поиск при величине
модуля, сравнимого с общим
числом элементов
Сравнительно быстрая вставка и
удаление элементов
Возможность работы с
неупорядоченными ключами
Недостатки



Итерация не в порядке
возрастания ключей
Слияние только поэлементное
Необходимость
«перехеширования» при
увеличении числа хранимых
объектов
java.util.HashMap, java.util.Hashtable – варианты реализации этой идеи.
Один из вариантов реализации
public class HashDictionary {
private int HASH_MODULE = 13;
// Начальное значение хеш-модуля
// Хеш-функция – скорректированная сумма кодов символов строки
private int hash(String key) {
int s = 0;
for (int i = 0; i < key.length(); i++) {
s += key.charAt(i) + i + 1;
s %= HASH_MODULE;
}
return s;
}
// Узел списка – элемент контейнера
private static class ListNode {
String key;
// ключ поиска
Object obj;
// ассоциированный объект
ListNode next; // указатель на следующий элемент
// Два варианта конструктора
ListNode(String key, Object obj, ListNode next) {
this.key = key; this.obj = obj; this.next = next;
}
ListNode(String key, Object obj) {
this(key, obj, null);
}
}
// Счетчик количества элементов
private int count = 0;
// Массив контейнеров-списков
private ListNode[] container = new ListNode[HASH_MODULE];
Один из вариантов реализации (продолжение)
public Object get(String key) {
int hash = hash(key);
// значение хеш-функции
ListNode current = container[hash];
// для поиска элемента в контейнере
for (current != null && !current.key.equals(key); current = current.next)
;
return current == null ? null : current.obj;
}
public Object put(String key, Object value) {
if (value == null) throw new NullPointerException("HashDictionary.put");
// value != null
int hash = hash(key);
// значение хеш-функции
ListNode current = container[hash];
// для поиска элемента в контейнере
for (current != null && !current.key.equals(key); current = current.next)
;
if (current == null) { // ключ не найден – вставляем новый элемент списка
container[hash] = new ListNode(key, value, container[hash]);
if (++count > 2*HASH_MODULE) rehash();
// перехеширование, если необходимо!
return null;
} else {
// ключ найден – заменяем ассоциированный объект
Object tmp = current.obj;
current.obj = value;
return tmp;
}
}
public Object remove(String key) {
int hash = hash(key);
// значение хеш-функции
ListNode current = container[hash];
// для поиска элемента в контейнере
ListNode prev = null;
// предыдущий элемент списка (если есть)
for (current = container[hash]; current != null && !current.key.equals(key); current = current.next)
prev = current;
if (current == null) {
// ключ не найден – удалять нечего
return null;
} else {
// ключ найден – удаляем элемент списка и возвращаем старое значение
if (prev == null) container[hash] = current.next; else prev.next = current.next;
count--;
return current.obj;
}
}
}
Download