← Назад к вопросам
Как устроена хеш-таблица (HashMap)?
2.2 Middle🔥 61 комментариев
#Dart#ООП и паттерны
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как устроена хеш-таблица (HashMap)?
Хеш-таблица (HashMap) — это одна из самых важных структур данных, широко используемая в Dart и Flutter для эффективного хранения и поиска данных. Понимание её работы критично для оптимизации приложений.
Принцип работы
Хеш-таблица основана на хеш-функции — функции, которая преобразует ключ в индекс массива:
Map<String, int> map = {
"apple": 5,
"banana": 3,
"cherry": 8,
};
print(map["apple"]); // O(1) в среднем случае
Процесс:
- Вычисляется хеш ключа: hash = hashCode(key)
- Вычисляется индекс в таблице: index = hash % tableSize
- По индексу ищется значение
Коллизии (Collisions)
Когда два разных ключа дают одинаковый индекс, возникает коллизия.
Методы разрешения коллизий:
1. Chaining (цепочки)
class HashTable {
List<List<MapEntry<String, int>>> table = List.generate(16, (_) => []);
void set(String key, int value) {
int hash = key.hashCode;
int index = hash % table.length;
for (var entry in table[index]) {
if (entry.key == key) {
entry = MapEntry(key, value);
return;
}
}
table[index].add(MapEntry(key, value));
}
int? get(String key) {
int hash = key.hashCode;
int index = hash % table.length;
for (var entry in table[index]) {
if (entry.key == key) return entry.value;
}
return null;
}
}
2. Open Addressing
class OpenAddressingHashTable {
List<MapEntry<String, int>?> table = List.filled(16, null);
void set(String key, int value) {
int hash = key.hashCode;
int index = hash % table.length;
while (table[index] != null && table[index]!.key != key) {
index = (index + 1) % table.length;
}
table[index] = MapEntry(key, value);
}
}
Хеш-функция
Хорошая хеш-функция:
- Детерминирована — один ключ всегда даёт одинаковый хеш
- Быстра — вычисляется за O(1)
- Равномерно распределяет — минимизирует коллизии
- Непредсказуема — сложно создать коллизию
class User {
final String id;
final String name;
User(this.id, this.name);
@override
int get hashCode => id.hashCode ^ name.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is User &&
runtimeType == other.runtimeType &&
id == other.id &&
name == other.name;
}
final map = <User, String>{};
final user = User("1", "John");
map[user] = "admin";
print(map[user]); // O(1) поиск
Resize (Расширение таблицы)
Когда таблица заполняется, происходит расширение в 2 раза:
class DynamicHashTable {
List<List<MapEntry<String, int>>> table = List.generate(16, (_) => []);
int _size = 0;
void set(String key, int value) {
int hash = key.hashCode;
int index = hash % table.length;
table[index].add(MapEntry(key, value));
_size++;
if (_size / table.length > 0.75) {
_resize();
}
}
void _resize() {
List<List<MapEntry<String, int>>> oldTable = table;
table = List.generate(oldTable.length * 2, (_) => []);
_size = 0;
for (var bucket in oldTable) {
for (var entry in bucket) {
set(entry.key, entry.value);
}
}
}
}
Использование в Dart
Map — стандартная реализация хеш-таблицы:
final map = <String, int>{};
map["count"] = 5; // O(1)
print(map["count"]); // O(1)
map.remove("count"); // O(1)
final linkedMap = <String, int>{};
linkedMap["a"] = 1;
linkedMap["b"] = 2;
for (var key in linkedMap.keys) print(key);
Временная сложность
- Поиск (get) — O(1) в среднем, O(n) в худшем
- Вставка (set) — O(1) в среднем
- Удаление (remove) — O(1) в среднем
- Итерация — O(n)
Лучшие практики
- Используйте Map для быстрого поиска
- Правильно реализуйте hashCode и ==
- Избегайте частого resize
- Не меняйте ключи после вставки
Хеш-таблица — фундаментальная структура данных для эффективной разработки.