«1. Обзор

В этом кратком руководстве мы сосредоточимся на основных различиях между Hashtable и HashMap.

2. Hashtable и HashMap в Java

Hashtable и HashMap очень похожи — обе коллекции реализуют интерфейс Map.

Кроме того, методы put(), get(), remove() и containsKey() обеспечивают производительность O(1) с постоянным временем. Внутри эти методы работают на основе общей концепции хеширования с использованием сегментов для хранения данных.

Ни один из классов не поддерживает порядок вставки элементов. Другими словами, первый добавленный элемент может не быть первым элементом, когда мы перебираем значения.

Но у них также есть некоторые различия, которые делают один лучше другого в некоторых ситуациях. Давайте рассмотрим эти различия поближе.

3. Различия между Hashtable и HashMap

3.1. Синхронизация

Во-первых, Hashtable является потокобезопасным и может совместно использоваться несколькими потоками в приложении.

С другой стороны, HashMap не синхронизируется и не может быть доступен нескольким потокам без дополнительного кода синхронизации. Мы можем использовать Collections.synchronizedMap() для создания потокобезопасной версии HashMap. Мы также можем просто создать собственный код блокировки или сделать код потокобезопасным, используя ключевое слово synchronized.

HashMap не синхронизирован, поэтому он быстрее и использует меньше памяти, чем Hashtable. Как правило, несинхронизированные объекты работают быстрее, чем синхронизированные, в однопоточном приложении.

3.2. Нулевые значения

Еще одно отличие — обработка нулевых значений. HashMap позволяет добавить одну запись с нулем в качестве ключа, а также множество записей с нулем в качестве значения. Напротив, Hashtable вообще не допускает null. Давайте посмотрим на пример null и HashMap:

HashMap<String, String> map = new HashMap<String, String>();
map.put(null, "value");
map.put("key1", null);
map.put("key2", null);

Это приведет к:

assertEquals(3, map.size());

Далее, давайте посмотрим, чем отличается Hashtable:

Hashtable<String, String> table = new Hashtable<String, String>();
table.put("key", null);

Это приводит к исключению NullPointerException. Добавление объекта с нулевым ключом также приводит к NullPointerException:

table.put(null, "value");

3.3. Итерация по элементам

HashMap использует Iterator для перебора значений, тогда как Hashtable имеет Enumerator для того же. Iterator является преемником Enumerator, который устраняет его несколько недостатков. Например, у Iterator есть метод remove() для удаления элементов из базовых коллекций.

Iterator — отказоустойчивый итератор. Другими словами, он генерирует исключение ConcurrentModificationException, когда базовая коллекция изменяется во время итерации. Давайте посмотрим на пример отказоустойчивости:

HashMap<String, String> map = new HashMap<String, String>();
map.put("key1", "value1");
map.put("key2", "value2");

Iterator<String> iterator = map.keySet().iterator();
while(iterator.hasNext()){ 
    iterator.next();
    map.put("key4", "value4");
}

Это вызывает исключение ConcurrentModificationException, потому что мы вызываем put() во время итерации по коллекции.

4. Когда выбирать HashMap, а не Hashtable

Мы должны использовать HashMap для несинхронизированного или однопоточного приложения.

Стоит отметить, что начиная с JDK 1.8, Hashtable устарела. Однако ConcurrentHashMap — отличная замена Hashtable. Мы должны рассмотреть ConcurrentHashMap для использования в приложениях с несколькими потоками.

5. Заключение

В этой статье мы проиллюстрировали различия между HashMap и Hashtable и то, что нужно иметь в виду, когда нам нужно выбрать один из них.

Как обычно, реализация всех этих примеров и фрагментов кода завершена на Github.