Ответ
Для работы с TreeSet<E> (который реализует интерфейс SortedSet) элементы должны быть сравнимыми. Это достигается одним из двух способов:
1. Класс реализует интерфейс Comparable<E>
- В классе должен быть реализован метод
int compareTo(E o). TreeSetбудет использовать этот метод для сортировки и определения уникальности.
2. При создании TreeSet передается объект Comparator<E>
- Это внешний компаратор, который определяет порядок элементов независимо от их собственной реализации
Comparable.
Строгое правило: Согласованность с equals()
Метод compareTo() (или Comparator.compare()) должен быть согласован с equals(). Это означает:
if (a.compareTo(b) == 0) {
// тогда a.equals(b) должно возвращать true
}
Нарушение этого правила приведет к странному поведению TreeSet, так как он использует сравнение для проверки уникальности, а не equals().
Пример 1: Использование Comparable
class Person implements Comparable<Person> {
private String name;
private int age;
@Override
public int compareTo(Person other) {
// Сравниваем сначала по имени, потом по возрасту
int nameCompare = this.name.compareTo(other.name);
return (nameCompare != 0) ? nameCompare : Integer.compare(this.age, other.age);
}
@Override
public boolean equals(Object o) { /* должна быть согласована с compareTo */ }
@Override
public int hashCode() { /* ... */ }
}
// TreeSet будет использовать compareTo
TreeSet<Person> set = new TreeSet<>();
Пример 2: Использование Comparator
// Создаем TreeSet с компаратором, сортирующим только по возрасту
TreeSet<Person> setByAge = new TreeSet<>(
Comparator.comparingInt(Person::getAge)
);
// Если два человека разного имени, но одного возраста, компаратор вернет 0.
// TreeSet сочтет их равными и не добавит второго, даже если equals() вернет false!
Практический вывод: Всегда следите, чтобы логика сравнения в compareTo() или Comparator соответствовала логике equals(), особенно при использовании в TreeSet или как ключа в TreeMap.