JavaRush/Курсы/Модуль 1. Java Syntax/Знакомство с коллекцией TreeMap

Знакомство с коллекцией TreeMap

Открыта

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

Что такое TreeMap? Это структура данных, похожая на словарь, которая позволяет сохранять элементы в виде ключ-значение, используя сортировку по ключам.

Где и как его можно использовать? Например, в том же случае с одногруппниками. Если мне нужно хранить значения в порядке возрастания, вместо того, чтобы писать свои алгоритмы сортировки, достаточно создать TreeMap и положить значения туда.

Для таких типов как Integer и String используется порядок сортировки по возрастанию. Но если ты хочешь положить в TreeMap собственный тип, нужно, чтобы твой класс имплементировал интерфейс Comparable, в классе был реализован метод compareTo() и указано, как нужно сортировать значения данного класса.

public class Person implements Comparable<Person> {

    private String firstName;
    private String lastName;

    public Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }.

    @Override
    public int compareTo(Person person) {
        return person.getFirstName().compareTo(firstName);
    }

    @Override
    public String toString() {
        return "Person{" +
                "firstName='" + firstName + '\'' +
                ", lastName='" + lastName + '\'' +
                '}';
    }
}

Переопределим метод compareTo(), чтобы он сортировал значение в порядке алфавитного убывания имени:

TreeMap map = new TreeMap<Person, String>();

map.put(new Person("AA","BB"), "aa");
map.put(new Person("BB","BB"), "aa");
map.put(new Person("DD","BB"), "aa");
map.put(new Person("CC","BB"), "aa");

Значения будут храниться в следующем порядке:

Person{firstName='DD', lastName='BB'}
Person{firstName='CC', lastName='BB'}
Person{firstName='BB', lastName='BB'}
Person{firstName='AA', lastName='BB'}

Класс TreeMap реализует интерфейс NavigableMap, который в свою очередь расширяет SortedMap. Это позволяет классу TreeMap использовать для хранения деревья и хранить значения в отсортированном порядке.

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

Если говорить простыми словами, красно-чёрное дерево — это структура данных, хранящая справа значения, которые больше корневого, а слева — те, что меньше. Такая реализация помогает быстрее искать значения в структуре.

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

Итак, теперь ты знаешь, что такое TreeMap и по какому принципу оно работает.

Важно помнить то, что хранить в TreeMap можно только тех, кто имплементировал Comparable интерфейс и переопределил метод compareTo().

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

Comparator — это интерфейс, в котором есть метод compare(). С его помощью мы можем сравнивать объекты и хранить их в TreeMap.

Comparator<Person> comparator = new Comparator<Person>() {

    @Override
    public int compare(Person person1, Person person2) {
        return person1.getFirstName().compareTo(person2.getFirstName());
    }
};


TreeMap map = new TreeMap<Person, String>(comparator);

В этом примере мы создали кастомный Comparator и передали TreeMap классу.

Начиная с Java 8 это можно написать, используя лямбда-выражение:

TreeMap map = new TreeMap<Person, String>((Person person1, Person person2) -> person1.getFirstName().compareTo(person2.getFirstName()) );

То есть, чтобы хранить значения в TreeMap, нужно указать, как их сортировать. Сделать это можно двумя способами: имплементировать Comparable или реализовать свой Comparator.

Но что если нам нужно положить в TreeMap значение null как ключ? HashMap позволяет это сделать. А как же работает с этим TreeMap?

TreeMap map = new TreeMap<Person, String>();
map.put (null, "Person");

Запустив этот код, получим ошибку:

Exception in thread "main" java.lang.NullPointerException at java.base/java.util.TreeMap.put(TreeMap.java:561)

Проблема заключается в том, что внутри класса TreeMap происходит сравнение значений методом compareTo(). Ты, конечно же, можешь положить туда null значение, и оно скомпилируется, но в рантайме ты получишь ошибку, так как на значении null вызовется метод и вылетит NullPointerException.

Сравнение HashMap и TreeMap

В отличие от TreeMap, в HashMap разрешено хранить null как ключ. Для всех null ключей выделено определенное место в структуре. Хранить null ключи в HashMap позволяет то, что определение места здесь происходит по значению хэша, и для этого не нужно сравнивать значения с другими. Поэтому у всех null ключей есть свое место.

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

Комментарии (55)
  • популярные
  • новые
  • старые
Для того, чтобы оставить комментарий Вы должны авторизоваться
Anton Petrov
Уровень 39
15 марта, 03:40
Интересно, а никого не смутило, что переменная так объявлена?
TreeMap map = new TreeMap<Person, String>();
А не лучше ли все же так сделать?
TreeMap<Person, String> map = new TreeMap<>();
Anonymous #3613348
Уровень 105
29 октября 2025, 18:16
Если кому-то, как и мне, тяжело было продраться через объяснение что такое Comparable и Comparator, то основное - интерфейсы Comparable и Comparator в Java служат для сравнения объектов друг с другом. Интерфейс Comparable<T> используется для реализации естественного порядка сортировки объектов класса. Если класс реализует этот интерфейс, объекты этого класса автоматически сортируются определенным образом при использовании стандартных методов сортировки (Collections.sort(), Arrays.sort()). Методы интерфейса: Реализует метод compareTo(T other):public int compareTo(T o); Этот метод возвращает целое число, которое показывает порядок текущего объекта относительно другого объекта: Отрицательное значение — текущий объект меньше переданного, Ноль — объекты равны, Положительное значение — текущий объект больше переданного. Интерфейс Comparator<T> позволяет задать альтернативный способ сортировки объектов независимо от их естественного порядка. Это полезно, если необходимо временно изменить способ сортировки, сохранив естественный порядок, заданный в классе посредством Comparable. Методы интерфейса: Реализует метод compare(T o1, T o2):public int compare(T o1, T o2); Возвращаемое значение аналогично compareTo: отрицательное, ноль или положительное, показывающее относительный порядок двух сравниваемых объектов. Таким образом, выбор между этими двумя механизмами зависит от требований приложения: если нужен единый стандарт сортировки для всех экземпляров определенного типа объектов, лучше использовать Comparable. Если нужно иметь возможность гибко менять порядок сортировки в зависимости от ситуации, тогда подходит Comparator.
Константин
Уровень 52
25 сентября 2025, 19:52
"Начиная с Java 8 это можно написать, используя лямбда-выражение" Пупупуууу, всё понятно
Николай
Уровень 112
16 июня 2025, 14:48
"Значения будут храниться в следующем порядке:" порядок вроде же неправильно приведен? Если они будут сортироваться по firstName (переопределен compareTo), который сортируется по алфавиту, то первыми будут идти Person{firstName='AA', lastName='BB'}, разве нет?
Anton Petrov
Уровень 39
15 марта, 03:57
Нет, тут правильный порядок. Чтобы сделать сортировку, так как вы пишите, нужно переписать метод
@Override
public int compareTo(Person person) {
    //return person.getFirstName().compareTo(firstName);
	return firstName.compareTo(person.getFirstName());
}
Andrei A.
Уровень 115
22 марта 2025, 18:21
Итак дружок, если ты прочитал эту недолгую лекцию, то ты овладел знанием о TreeMap, Comparator, Comparable, Map>SortedMap>NavigableMap, Red–black tree, ... Go little rock star... ⭐🚀
Денис
Уровень 68
17 ноября 2024, 07:36
"Итак, теперь ты знаешь, что такое TreeMap". Нет, я не знаю, что такое TreeMap. В лекции информация представлена в сжатом и поверхностном виде.
Сергей
Уровень 35
18 июня 2024, 16:59
В задаче "Зарплаты и позиции" ориентируемся на НИЖНЮЮ планку в ЗП при выводе.
Олег
Уровень 106
Expert
28 мая 2024, 15:47
Я ничего не понял :D
Денис
Уровень 68
17 ноября 2024, 07:27
но ты уже эксперт уровня 100. Скорее всего понял).
Олег
Уровень 106
Expert
24 ноября 2024, 12:36
действительно, это оказалось очень простым и понятным, по сравнению с тем, что ждёт дальше)
Anton Rogulenko System Engineer Expert
18 апреля 2024, 11:56
Нет слов, одни возмущения, что за абстрактная информация, которая не дает нормального понимания. Читаем "Класс TreeMap реализует интерфейс NavigableMap, который в свою очередь расширяет SortedMap. Это позволяет классу TreeMap использовать для хранения деревья и хранить значения в отсортированном порядке. Дерево, как говорит Википедия, — это самобалансирующая двоичная структура поиска, для хранения данных, которая использует узлы для хранения, сравнивая перед этим значения. Если говорить простыми словами, красно-чёрное дерево — это структура данных, хранящая справа значения, которые больше корневого, а слева — те, что меньше. Такая реализация помогает быстрее искать значения в структуре. Красно-черное дерево самобалансирующееся, поэтому при каждой новой вставке оно будет менять свою структуру, так как добавляются новые значения. Корневым считается значение, добавленное первым, но в процессе балансировки, корневым может стать и другое значение." Как сходу понять учебный материал? По видимому материалы лекции не проверяют и соответствующие оценка этому разделу, тоже не является индикатором чтобы исправить, переработать лекцию.
ПавлоРаботает в EPAM
31 января 2024, 03:13
hint: try to use map.floorEntry() and pass salary as a parameter
dnekh
Уровень 46
Expert
4 июля 2024, 11:40
Крутая штука! Практически одна строка кода, помимо return