Другое

HashMap vs LinkedHashMap vs TreeMap: Полное руководство

Полное руководство по реализациям Java Map. Узнайте, как HashMap, LinkedHashMap и TreeMap отличаются по производительности, упорядочиванию и обработке null. Выберите подходящий Map для ваших нужд.

В чем разница между HashMap, LinkedHashMap и TreeMap в Java?

Я запутался в различиях между этими реализациями Map, так как у них, кажется, похожие методы, такие как keySet() и values(). Может кто-нибудь объяснить:

  1. В чем ключевые различия между HashMap, LinkedHashMap и TreeMap?
  2. Как отличаются их порядок вставки, сортировка и характеристики производительности?
  3. Что такое Hashtables и как они связаны с этими реализациями Map?

Вот некоторый код, с которым я экспериментировал:

java
Map<String, String> m1 = new HashMap<>();
m1.put("map", "HashMap");
m1.put("schildt", "java2");
m1.put("mathew", "Hyden");
m1.put("schildt", "java2s");
print(m1.keySet()); 
print(m1.values()); 

SortedMap<String, String> sm = new TreeMap<>();
sm.put("map", "TreeMap");
sm.put("schildt", "java2");
sm.put("mathew", "Hyden");
sm.put("schildt", "java2s");
print(sm.keySet()); 
print(sm.values());

LinkedHashMap<String, String> lm = new LinkedHashMap<>();
lm.put("map", "LinkedHashMap");
lm.put("schildt", "java2");
lm.put("mathew", "Hyden");
lm.put("schildt", "java2s");
print(lm.keySet()); 
print(lm.values());

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

HashMap, LinkedHashMap и TreeMap — это три фундаментальные реализации интерфейса Map в Java, которые различаются в основном своим поведением упорядочивания, характеристиками производительности и обработкой null-ключей и значений. HashMap обеспечивает наилучшую производительность без гарантии порядка, LinkedHashMap сохраняет порядок вставки со схожей производительностью, в то время как TreeMap гарантирует сортировку ключей, но за счет более медленных операций. Выбор между ними зависит от ваших конкретных требований к порядку, производительности и поддержке null-значений.

Содержание

Обзор основных различий

Три реализации Map служат разным целям, несмотря на то, что они реализуют один и тот же интерфейс:

  • HashMap: Наилучшая производительность, без гарантии порядка
  • LinkedHashMap: Сохраняет порядок вставки, хорошая производительность
  • TreeMap: Сохраняет отсортированный порядок, более медленная производительность

Эти различия обусловлены их базовыми структурами данных: HashMap использует хеш-таблицу, LinkedHashMap использует хеш-таблицу с двусвязным списком, а TreeMap использует красно-черное дерево.

Характеристики HashMap

HashMap — наиболее часто используемая реализация Map в Java. Она обеспечивает постоянную временную сложность O(1) для базовых операций, таких как put() и get() в нормальных условиях. Согласно официальной документации, HashMap не поддерживает никакого порядка элементов.

Основные характеристики:

  • Нет гарантии порядка для ключей или значений
  • O(1) средняя временная сложность для операций get/put
  • Позволяет один null-ключ и несколько null-значений
  • Не синхронизирован (не потокобезопасен)
  • Начальная емкость 16 с коэффициентом загрузки 0.75

В вашем примере кода при выводе m1.keySet() и m1.values() порядок вывода будет непредсказуемым, что является типичным поведением HashMap.

Особенности LinkedHashMap

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

Основные характеристики:

  • Сохраняет порядок вставки элементов
  • O(1) временная сложность для операций get/put
  • Позволяет один null-ключ и несколько null-значений
  • Незначительно больше накладных расходов на память, чем у HashMap, из-за двусвязного списка
  • Предсказуемый порядок итерации

В вашем коде lm.keySet() и lm.values() будут возвращать элементы в порядке их вставки, что делает LinkedHashMap идеальным для сценариев, где важен порядок, но производительность все еще важна.

Поведение TreeMap

TreeMap реализует интерфейс SortedMap и сохраняет ключи в отсортированном порядке в соответствии с их естественным порядком или указанным Comparator. На сайте Testbook.com отмечается, что TreeMap хранит ключи в отсортированном и возрастающем порядке со сложностью O(logN) для операций вставки и извлечения.

Основные характеристики:

  • Сохраняет ключи в отсортированном порядке (естественном или пользовательском)
  • O(log n) временная сложность для операций get/put
  • Не позволяет null-ключи (но позволяет null-значения)
  • Предоставляет методы для диапазонных операций
  • Использует структуру данных красно-черное дерево

В вашем примере кода sm.keySet() и sm.values() будут возвращать элементы, отсортированные алфавитно по ключам, что является определяющим поведением TreeMap.

Сравнение производительности

Различия в производительности между этими реализациями Map существенны:

Операция HashMap LinkedHashMap TreeMap
get() O(1) O(1) O(log n)
put() O(1) O(1) O(log n)
remove() O(1) O(1) O(log n)
containsKey() O(1) O(1) O(log n)
Итерация O(n) O(n) O(n)

Как объясняется на Java67, “HashMap обеспечивает наилучшую производительность, так как нет накладных расходов, TreeMap дает более медленную производительность, потому что каждый раз при добавлении или удалении сопоставления ему нужно сортировать всю карту. LinkedHashMap обеспечивает производительность между ними”.

На самом деле, LinkedHashMap итерируется быстрее, чем HashMap из-за своей базовой структуры, что делает его более эффективным для частых операций итерации.

Обработка null-ключей и значений

Обработка null-ключей и значений существенно различается:

  • HashMap: Позволяет один null-ключ и несколько null-значений
  • LinkedHashMap: То же, что и HashMap (один null-ключ, несколько null-значений)
  • TreeMap: Не позволяет null-ключи, потому что метод compareTo() вызовет NullPointerException, но позволяет null-значения
  • Hashtable: Вообще не позволяет null-ключи или значения

На Stack Overflow уточняется, что “HashMap не поддерживает никакого порядка” и “TreeMap (интерфейс SortedMap) - определенный порядок, который я определяю”.

Hashtable vs HashMap семейство

Hashtable — это устаревший класс, который существенно отличается от семейства HashMap:

Характеристики Hashtable:

  • Синхронизирован (потокобезопасен)
  • Не позволяет null-ключи или значения
  • Расширяет класс Dictionary, а не AbstractMap
  • Более медленная производительность из-за синхронизации
  • Совместимость с устаревшим кодом

Согласно W3schools, “Он расширяет класс Dictionary и реализует интерфейсы Map, Cloneable, Serializable. Он использует метод hashcode() для поиска позиции объектов/элементов. Hashtable синхронизирован. Он не может содержать null-ключ или значение”.

Когда использовать каждую реализацию

Выбирайте HashMap, когда:

  • Производительность является основным приоритетом
  • Порядок элементов не имеет значения
  • Вам нужно хранить null-ключи или значения
  • Работа в однопоточной среде

Выбирайте LinkedHashMap, когда:

  • Вам нужно поддерживать порядок вставки
  • Производительность все еще важна
  • Вам нужно часто итерировать по карте
  • Вам нужна поддержка null-ключей/значений с порядком

Выбирайте TreeMap, когда:

  • Вам нужен отсортированный порядок (естественный или пользовательский)
  • Вам нужны диапазонные операции (subMap, headMap, tailMap)
  • Использование памяти менее критично, чем порядок
  • Вам не нужны null-ключи

Выбирайте Hashtable, когда:

  • Вам нужна потокобезопасность без внешней синхронизации
  • Работа с устаревшим кодом
  • Null-значения не требуются

Практические примеры

На основе вашего примера кода, давайте проанализируем, что произойдет:

java
Map<String, String> m1 = new HashMap<>();
m1.put("map", "HashMap");
m1.put("schildt", "java2");
m1.put("mathew", "Hyden");
m1.put("schildt", "java2s");
print(m1.keySet()); // Порядок вывода непредсказуем - может быть любым
print(m1.values()); // Значения будут следовать тому же непредсказуемому порядку

SortedMap<String, String> sm = new TreeMap<>();
sm.put("map", "TreeMap");
sm.put("schildt", "java2");
sm.put("mathew", "Hyden");
sm.put("schildt", "java2s");
print(sm.keySet()); // Вывод: [mathew, map, schildt] (алфавитный порядок)
print(sm.values()); // Вывод: [Hyden, TreeMap, java2s] (следует порядку ключей)

LinkedHashMap<String, String> lm = new LinkedHashMap<>();
lm.put("map", "LinkedHashMap");
lm.put("schildt", "java2");
lm.put("mathew", "Hyden");
lm.put("schildt", "java2s");
print(lm.keySet()); // Вывод: [map, schildt, mathew] (порядок вставки, с обновлением дублирующего ключа)
print(lm.values()); // Вывод: [LinkedHashMap, java2s, Hyden] (следует порядку вставки)

Заключение

Понимание различий между HashMap, LinkedHashMap и TreeMap имеет решающее значение для написания эффективного Java-кода. HashMap предлагает наилучшую производительность без упорядочивания, LinkedHashMap обеспечивает поддержание порядка вставки с хорошей производительностью, в то время как TreeMap гарантирует отсортированные ключи за счет более медленных операций. Учитывайте ваши конкретные требования к порядку, производительности и потокобезопасности при выборе между этими реализациями. Помните, что Hashtable в основном предназначен для устаревшего кода и потокобезопасных сценариев, в то время как семейство HashMap предлагает больше гибкости и лучшую производительность для большинства современных приложений.

Источники

  1. Различия между HashMap, LinkedHashMap и TreeMap - Stack Overflow
  2. Различия между HashMap, LinkedHashMap и TreeMap в Java с примером - Java Revisited
  3. Различия между TreeMap, HashMap и LinkedHashMap в Java - GeeksforGeeks
  4. Java TreeMap vs HashMap - Baeldung
  5. HashMap vs TreeMap vs LinkedHashMap в Java - Java67
  6. HashMap vs LinkedHashMap vs TreeMap vs HashTable в Java - W3schools
  7. Производительность TreeMap, HashMap и LinkedHashMap - Stack Overflow
Авторы
Проверено модерацией
Модерация