0Pricing
Java Academy · Урок

Интерфейс Comparable

Реализуйте Comparable, чтобы задать классу естественный порядок, и используйте его вместе с Collections.sort.

«Интерфейс Comparable» — бесплатный урок Java Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Java Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Java Academy содержит 4 уроков всего.

Интерфейс естественного сравнения

Comparable<T> задаёт для класса естественный порядок. Реализуйте этот интерфейс, чтобы экземпляры можно было сортировать с помощью Collections.sort(), Arrays.sort() и упорядоченных коллекций, таких как TreeSet.

Реализация интерфейса сравнения

Реализуйте compareTo(T other), возвращая отрицательное значение (текущий объект меньше аргумента), ноль (объекты равны) или положительное значение (текущий объект больше аргумента).

class Product implements Comparable<Product> {
    private final String name;
    private final double price;

    Product(String name, double price) {
        this.name = name; this.price = price;
    }

    @Override
    public int compareTo(Product other) {
        return Double.compare(this.price, other.price); // ascending by price
    }

    @Override public String toString() { return name + "($" + price + ")"; }
}

List<Product> products = new ArrayList<>(List.of(
    new Product("Mouse", 29.99),
    new Product("Laptop", 999.0),
    new Product("Keyboard", 79.99)
));
Collections.sort(products);
System.out.println(products); // [Mouse($29.99), Keyboard($79.99), Laptop($999.0)]

Контракт compareTo

Правильная реализация интерфейса сравнения должна соблюдать контракт:

  • Антисимметрия: sgn(a.compareTo(b)) == -sgn(b.compareTo(a))
  • Транзитивность: если a > b и b > c, то a > c
  • Согласованность: a.compareTo(b) == 0 означает a.equals(b); это настоятельно рекомендуется

Безопасное сравнение примитивов

Никогда не вычитайте примитивы в compareTo: переполнение целого числа может привести к неправильному результату. Используйте Integer.compare(), Double.compare() и другие подобные методы.

// WRONG: integer subtraction can overflow
int compareTo(Player other) {
    return this.score - other.score; // overflow if scores differ by > Integer.MAX_VALUE
}

// CORRECT: use Integer.compare
int compareTo(Player other) {
    return Integer.compare(this.score, other.score);
}

// For strings: delegate to String.compareTo
int compareTo(Player other) {
    return this.name.compareTo(other.name); // String handles it correctly
}

Естественный порядок в TreeSet

Классы, реализующие интерфейс естественного сравнения, автоматически размещаются в упорядоченных коллекциях, таких как TreeSet и TreeMap.

class Priority implements Comparable<Priority> {
    enum Level { LOW, MEDIUM, HIGH, CRITICAL }
    final Level level;
    final String task;
    Priority(Level level, String task) { this.level = level; this.task = task; }
    @Override
    public int compareTo(Priority other) {
        return this.level.compareTo(other.level); // enum ordinal order
    }
    @Override public String toString() { return level + ": " + task; }
}

TreeSet<Priority> queue = new TreeSet<>();
queue.add(new Priority(Priority.Level.CRITICAL, "Fix prod crash"));
queue.add(new Priority(Priority.Level.LOW, "Update docs"));
queue.add(new Priority(Priority.Level.HIGH, "Deploy feature"));
queue.forEach(System.out::println);
// LOW: Update docs
// HIGH: Deploy feature
// CRITICAL: Fix prod crash

Сравнение по нескольким полям

Чтобы сортировать по нескольким полям, объединяйте сравнения в цепочку: сначала основное, затем вторичное, если основные значения равны.

class Employee implements Comparable<Employee> {
    final String dept, name;
    final double salary;

    Employee(String dept, String name, double salary) {
        this.dept = dept; this.name = name; this.salary = salary;
    }

    @Override
    public int compareTo(Employee other) {
        int deptCmp = this.dept.compareTo(other.dept);
        if (deptCmp != 0) return deptCmp;           // primary: by dept
        return this.name.compareTo(other.name);      // secondary: by name
    }
}

Согласованность сравнения и equals

Настоятельно рекомендуется (хотя это и не обязательно), чтобы a.compareTo(b) == 0 выполнялось тогда и только тогда, когда a.equals(b). Нарушение этого правила приводит к трудноуловимым ошибкам в упорядоченных множествах и отображениях.

// BigDecimal violates this: new BigDecimal("2.0").compareTo(new BigDecimal("2.00")) == 0
// but new BigDecimal("2.0").equals(new BigDecimal("2.00")) == false

// This causes TreeSet to treat them as equal (only one stored)
TreeSet<java.math.BigDecimal> set = new TreeSet<>();
set.add(new java.math.BigDecimal("2.0"));
set.add(new java.math.BigDecimal("2.00"));
System.out.println(set.size()); // 1 — compareTo-equal → same element

Сортировка с помощью Collections.sort

Collections.sort() и Arrays.sort() используют естественный порядок, определённый интерфейсом сравнения.

List<String> names = new ArrayList<>(List.of("Charlie", "Alice", "Bob"));
Collections.sort(names); // natural alphabetical order
System.out.println(names); // [Alice, Bob, Charlie]

String[] arr = {"banana", "apple", "cherry"};
Arrays.sort(arr);
System.out.println(Arrays.toString(arr)); // [apple, banana, cherry]

// Stream sorted() uses natural order
names.stream().sorted().forEach(System.out::println);

Интерфейс сравнения в двоичном поиске

Collections.binarySearch() требует, чтобы список был отсортирован в естественном порядке, а его элементы реализовывали интерфейс сравнения.

List<Integer> sorted = new ArrayList<>(List.of(1, 3, 5, 7, 9, 11));
int idx = Collections.binarySearch(sorted, 7);
System.out.println("Found 7 at index: " + idx); // 3

int missing = Collections.binarySearch(sorted, 4);
System.out.println("4 not found, insertion point: " + (-missing - 1)); // 2

Сравнение объектов и Comparator

Главное различие:

  • Интерфейс сравнения: задаёт собственный естественный порядок класса — один порядок для каждого класса
  • Comparator: задаёт внешний порядок — таких вариантов может быть сколько угодно, и их можно объединять

Практика: таблица лидеров

Таблица лидеров с естественным порядком по убыванию баллов.

class LeaderboardEntry implements Comparable<LeaderboardEntry> {
    final String player;
    final int score;
    final long timestamp;

    LeaderboardEntry(String player, int score) {
        this.player = player; this.score = score;
        this.timestamp = System.nanoTime();
    }

    @Override
    public int compareTo(LeaderboardEntry other) {
        int scoreCmp = Integer.compare(other.score, this.score); // descending
        if (scoreCmp != 0) return scoreCmp;
        return Long.compare(this.timestamp, other.timestamp); // earlier = higher
    }

    @Override public String toString() { return player + ": " + score; }
}

TreeSet<LeaderboardEntry> board = new TreeSet<>();
board.add(new LeaderboardEntry("Alice", 950));
board.add(new LeaderboardEntry("Bob", 1200));
board.add(new LeaderboardEntry("Carol", 950));
board.forEach(System.out::println);
// Bob: 1200 / Alice: 950 / Carol: 950

Быстрая проверка

Что возвращает compareTo(), если текущий объект меньше аргумента?

Итоги: интерфейс естественного сравнения

Главные выводы:

  • Реализуйте интерфейс сравнения, чтобы определить естественный порядок класса
  • compareTo возвращает отрицательное значение (меньше), ноль (равно) или положительное значение (больше)
  • Используйте Integer.compare()/Double.compare(), а не вычитание: возможен риск переполнения
  • Для сортировки по нескольким полям объединяйте сравнения в цепочку: основное → вторичное
  • Естественный порядок используется в Collections.sort, Arrays.sort, TreeSet и TreeMap
  • Интерфейс сравнения определяет ONE порядок; для нескольких порядков используйте Comparator

Часто задаваемые вопросы

Урок «Интерфейс Comparable» бесплатный?

Да — полный текст урока «Интерфейс Comparable» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Java Academy, подпишись на CoddyKit PRO. Курс Java Academy содержит 4 уроков всего.

Чему я научусь в уроке «Интерфейс Comparable»?

Реализуйте Comparable, чтобы задать классу естественный порядок, и используйте его вместе с Collections.sort. Ты практикуешь Java Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Java Academy?

Предыдущий опыт не требуется. Java Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.

Сколько времени занимает урок «Интерфейс Comparable»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Java Academy?

Да. Каждый урок Java Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Интерфейс Comparable
  2. Сортировка с Comparator и лямбда-выражениями
  3. Сортировка по нескольким ключам с thenComparing
  4. Сортировка массивов и коллекций на практике
← Назад к Java Academy