Сортировка коллекций
Сортировка в Java строится на двух интерфейсах:
Comparable<T>— «естественный порядок» самого типа. МетодcompareTo(T other). Например,Integer,String,LocalDateуже реализуютComparable.Comparator<T>— внешняя стратегия сравнения. Передаётся в сортировку как параметр. Удобно для нескольких разных порядков для одного и того же класса.
Контракт compareTo/compare: возвращает отрицательное число, если a < b; положительное, если a > b; ноль, если равны.
Утилиты для сортировки:
Collections.sort(List)/Collections.sort(List, Comparator)List.sort(Comparator)— современный способ для списковArrays.sort(...)Stream.sorted()/Stream.sorted(Comparator)
Пример 1. Естественный порядок (Comparable)
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class SortNatural {
public static void main(String[] args) {
List<Integer> data = new ArrayList<>();
data.add(42); data.add(7); data.add(19); data.add(3);
Collections.sort(data);
System.out.println("По возрастанию: " + data);
Collections.sort(data, Collections.reverseOrder());
System.out.println("По убыванию: " + data);
}
}
Output:
По возрастанию: [3, 7, 19, 42]
По убыванию: [42, 19, 7, 3]
Пример 2. Свой класс с Comparable
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class SortComparable {
static class Sensor implements Comparable<Sensor> {
String name;
int distance;
Sensor(String n, int d) { name = n; distance = d; }
@Override public int compareTo(Sensor o) {
return Integer.compare(this.distance, o.distance);
}
@Override public String toString() { return name + "=" + distance; }
}
public static void main(String[] args) {
List<Sensor> list = new ArrayList<>();
list.add(new Sensor("front", 45));
list.add(new Sensor("left", 12));
list.add(new Sensor("right", 87));
Collections.sort(list);
System.out.println(list);
}
}
Output:
[left=12, front=45, right=87]
Пример 3. Comparator.comparing и thenComparing
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public class SortComparator {
record Kit(String name, int price, int year) {}
public static void main(String[] args) {
List<Kit> kits = new ArrayList<>();
kits.add(new Kit("Phobo", 25000, 2024));
kits.add(new Kit("UNO", 12000, 2023));
kits.add(new Kit("UNO", 12000, 2024));
kits.add(new Kit("ESP32", 18000, 2024));
Comparator<Kit> byNameThenYear =
Comparator.comparing(Kit::name).thenComparingInt(Kit::year);
kits.sort(byNameThenYear);
kits.forEach(System.out::println);
}
}
Output:
Kit[name=ESP32, price=18000, year=2024]
Kit[name=Phobo, price=25000, year=2024]
Kit[name=UNO, price=12000, year=2023]
Kit[name=UNO, price=12000, year=2024]
Пример 4. reversed и nullsFirst
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
public class SortReverseNulls {
public static void main(String[] args) {
List<String> data = new ArrayList<>(Arrays.asList("Phobo", null, "UNO", "ESP32", null));
data.sort(Comparator.nullsFirst(Comparator.naturalOrder()));
System.out.println("nullsFirst: " + data);
data.sort(Comparator.nullsLast(Comparator.<String>naturalOrder().reversed()));
System.out.println("nullsLast+reversed: " + data);
}
}
Output:
nullsFirst: [null, null, ESP32, Phobo, UNO]
nullsLast+reversed: [UNO, Phobo, ESP32, null, null]
Пример 5. Stream.sorted и сбор в новый список
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
public class SortStream {
public static void main(String[] args) {
List<String> kits = Arrays.asList("UNO", "Phobo", "ESP32", "Nano");
List<String> byLen = kits.stream()
.sorted(Comparator.comparingInt(String::length).thenComparing(Comparator.naturalOrder()))
.collect(Collectors.toList());
System.out.println(byLen);
}
}
Output:
[UNO, Nano, ESP32, Phobo]
Пример 6. TreeSet с Comparator
import java.util.Comparator;
import java.util.TreeSet;
public class SortTreeSet {
public static void main(String[] args) {
TreeSet<String> kits = new TreeSet<>(
Comparator.comparingInt(String::length).thenComparing(Comparator.naturalOrder())
);
kits.add("UNO");
kits.add("Phobo");
kits.add("ESP32");
kits.add("Nano");
kits.add("Mega");
System.out.println(kits);
}
}
Output:
[UNO, Mega, Nano, ESP32, Phobo]
Подводные камни
compareToобязан быть согласован с ``equals``, иначеTreeSet/TreeMapбудут вести себя странно.Никогда не пишите
return a.value - b.value;дляint— возможен переполнение. ИспользуйтеInteger.compare(a, b).Collections.sortстабилен (равные элементы сохраняют относительный порядок).Сортировать
List, возвращённыйArrays.asList, можно (sortне меняет размер), а вотList.of(...)— нет (броситUnsupportedOperationException).Comparator.naturalOrder()требует, чтобы тип реализовывалComparable.При сравнении строк по содержимому — учтите регистр (
String.CASE_INSENSITIVE_ORDER) и локаль (Collator).
См. также
Примечание
Материал подготовлен на основе официального руководства Oracle The Collections Framework и распространяется на условиях Oracle Free Documentation License. Тексты переписаны своими словами, примеры кода — оригинальные.