Вы обычно используете HashSet
или HashMap
реализацию, когда вам нужно использовать Set
или Map
? Вы знаете про TreeMap
или TreeSet
? Если нет, то вам повезло, потому что мы рассмотрим, что это такое, почему и когда они могут быть полезны.
Реализация и сложность
Подобно тому, как HashSet
реализуется с использованием HashMap
, TreeSet
реализуется с использованием TreeMap
. Сам TreeMap
реализован с использованием красно-черного дерева, которое представляет собой самобалансирующееся двоичное дерево поиска. Поскольку в нем используется двоичное дерево, операции put()
, contains()
и remove()
имеют временную сложность O (log n). Кроме того, поскольку дерево сбалансировано, временная сложность наихудшего случая также составляет O (log n).
С другой стороны, HashMap
имеет среднюю временную сложность O (1) для операций put()
, contains()
и remove()
. Наихудшая временная сложность для этих операций составляет O (log n) с Java 8 и O (n) до этого.
С точки зрения пространственной сложности оба имеют сложность O (n). Однако TreeMap
более экономичен, чем HashMap
, потому что по умолчанию HashMap
заполнен не более чем на 75%, чтобы избежать слишком большого количества коллизий. В результате остается неиспользуемое пространство. Это можно настроить с помощью коэффициента загрузки.
O (1) лучше O (log n), так зачем нам использовать что-то более медленное?
Навигация и сортировка
TreeMap
и TreeSet
равны Navigable
и Sorted
, что не относится к HashMap
и HashSet
. По умолчанию порядок является естественным, однако его можно изменить, указав Comparator
в конструкторе.
NavigableSet
Интерфейс NavigableSet
предлагает множество очень удобных методов:
lower(e)
: возвращает наибольший элемент, строго меньший, чемe
, илиnull
, если его нетfloor(e)
: то же, что иlower(e)
, но включает элементы, равныеe
higher(e)
: возвращает наименьший элемент, строго превышающийe
, илиnull
, если его нетceiling(e)
: то же, что иhigher(e)
, но включает элементы, равныеe
pollFirst()
иpollLast()
: возвращает наименьший и наибольший элементы соответственно илиnull
, если набор пуст
Давайте посмотрим на несколько примеров:
NavigableMap
NavigableMap
предлагает те же методы, но с суффиксом Entry
: lowerEntry(e)
, higherEntry(e)
и т. Д. Порядок основан на ключах карты:
SortedSet
Благодаря этому интерфейсу у нас есть доступ к следующим функциям:
subSet(a, b)
: возвращает подмножество элементов, которые больше или равныa
и строго меньшеb
.headSet(e)
: возвращает набор элементов, которые строго меньше, чемe
tailSet(e)
: возвращает набор элементов, которые больше или равныe
first()
иlast()
: возвращает наименьший и наибольший элементы соответственно
Опять же, пример, вероятно, будет иметь гораздо больше смысла:
SortedMap
Что касается NavigableMap
, методы такие же, как SortedSet
, но с суффиксом Map
. Поведение такое же, и для упорядочивания используются ключи карты.
Итерация
Интерфейс Sorted
дает одно главное преимущество TreeMap
и TreeSet
: итерация будет следовать отсортированному порядку. Когда дело доходит до HashSet
или HashMap
, порядок будет хеш-кодами, а не элементами. Это различие может быть критерием выбора одной реализации по сравнению с другой. Можно создать отсортированный контейнер из HashSet
или HashMap
, но сортировка обычно имеет сложность 0 (n log n).
Параллелизм
Оба TreeMap
и TreeSet
не являются потокобезопасными. Для обеспечения многопоточности можно использовать Collections.synchronizedNavigableMap(treeMap)
, Collections.synchronizedSortedMap(treeMap)
или ConcurrentSkipListMap()
(замените Map
на Set
для Set
s). Он реализован с использованием списка пропуска вместо дерева, поскольку списки пропуска более эффективны в параллельных средах, но временные сложности и поведение остаются теми же.
Заключение
TreeMap
иTreeSet
более эффективны с точки зрения памяти по сравнению с ихHash
аналогами. Если память важнее скорости, тогда эта структура данных может быть хорошим выбором.TreeMap
иTreeSet
отсортированы и могут быть идеальным выбором, если вам нужно очень часто получать диапазоны или перебирать определенный порядок. Собственно, базы данных используют b-деревья в качестве индексов для ранжированных запросов по тем же причинам.
Надеюсь, вы узнали что-то новое, спасибо за чтение!