← Назад к вопросам

В чем разница между peek() и sorted()?

1.3 Junior🔥 181 комментариев
#Stream API и функциональное программирование#Основы Java

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Peek() vs Sorted() в Java Stream API

Основное различие

peek() — промежуточная операция для отладки/логирования, не изменяет Stream.

sorted() — промежуточная операция для сортировки, изменяет порядок элементов.

Оба — промежуточные операции (intermediate), но имеют разные назначение и поведение.

Peek() — операция для отладки

List<Integer> numbers = List.of(3, 1, 4, 1, 5);

List<Integer> result = numbers.stream()
    .peek(n -> System.out.println("Original: " + n))  // Видит каждый элемент
    .map(n -> n * 2)
    .peek(n -> System.out.println("After map: " + n))
    .toList();

// Вывод:
// Original: 3
// After map: 6
// Original: 1
// After map: 2
// Original: 4
// After map: 8
// Original: 1
// After map: 2
// Original: 5
// After map: 10

Характеристики peek():

  • Промежуточная операция (intermediate operation)
  • Не изменяет Stream
  • Ленивая (lazy) — выполняется только при terminal операции
  • Для отладки/логирования
  • Принимает Consumer<T> (void функцию)
  • Возвращает Stream без изменений
  • Элементы проходят через peek в том же порядке

Синтаксис peek()

Stream.peek(element -> { /* действие, не возвращает ничего */ });

// Примеры
stream.peek(System.out::println);
stream.peek(item -> logger.debug(item));
stream.peek(n -> counter.increment());

Когда использовать peek()

// ✅ Для отладки
List<User> users = userList.stream()
    .filter(u -> u.getAge() > 18)
    .peek(u -> System.out.println("Adult user: " + u.getName()))  // Отладка
    .toList();

// ✅ Для логирования
List<Product> products = productList.stream()
    .filter(p -> p.getPrice() > 100)
    .peek(p -> logger.info("Expensive product: " + p.getName()))
    .toList();

// ✅ Для сбора метрик
List<Integer> numbers = numberList.stream()
    .map(n -> n * 2)
    .peek(n -> metrics.recordValue(n))
    .toList();

// ❌ Не использовать для изменения состояния!
struct.peek(item -> item.setProcessed(true));  // Плохая практика

Sorted() — операция сортировки

List<Integer> numbers = List.of(3, 1, 4, 1, 5);

List<Integer> result = numbers.stream()
    .sorted()
    .toList();

System.out.println(result);  // [1, 1, 3, 4, 5]

Характеристики sorted():

  • Промежуточная операция (intermediate operation)
  • Изменяет порядок элементов в Stream
  • Требует буферизации — должна получить все элементы перед сортировкой
  • Stateful — требует состояния (весь Stream в памяти)
  • Ленивая (lazy) — но буферизирует при выполнении
  • Может принимать Comparator
  • Возвращает отсортированный Stream

Синтаксис sorted()

// Сортировка по умолчанию (естественный порядок)
Stream.sorted();

// Сортировка с Comparator
Stream.sorted(Comparator.comparingInt(o -> o));
Stream.sorted((a, b) -> a.compareTo(b));

// Примеры
stream.sorted();
stream.sorted(Comparator.naturalOrder());
stream.sorted(Comparator.reverseOrder());
stream.sorted(Comparator.comparingInt(String::length));
stream.sorted((a, b) -> b.compareTo(a));  // Обратный порядок

Примеры sorted()

List<String> words = Arrays.asList("apple", "banana", "cherry");

// Сортировка по алфавиту
List<String> sorted = words.stream()
    .sorted()
    .toList();
System.out.println(sorted);  // [apple, banana, cherry]

// Обратная сортировка
List<String> reversed = words.stream()
    .sorted(Comparator.reverseOrder())
    .toList();
System.out.println(reversed);  // [cherry, banana, apple]

// Сортировка по длине
List<String> byLength = words.stream()
    .sorted(Comparator.comparingInt(String::length))
    .toList();
System.out.println(byLength);  // [apple, banana, cherry]

// Сортировка по длине (обратно)
List<String> byLengthReverse = words.stream()
    .sorted(Comparator.comparingInt(String::length).reversed())
    .toList();
System.out.println(byLengthReverse);  // [banana, cherry, apple]

Детальное сравнение

АспектPeek()Sorted()
НазначениеОтладка, логированиеСортировка
Тип операцииIntermediate (промежуточная)Intermediate (промежуточная)
Изменяет StreamНетДа (порядок элементов)
ПараметрConsumer<T> (void функция)Comparator или ничего
ВозвращаетИсходный Stream без измененийОтсортированный Stream
ЛенивостьLazy (по требованию)Lazy (но требует буферизации)
ПамятиO(1) на элементO(n) (весь Stream)
ПроизводительностьO(n)O(n log n) обычно
Порядок элементовСохраняетсяИзменяется
СтатефулНетДа (требует состояния)
Используется в productionРедко (только отладка)Часто (функциональное)

Практические примеры

Peek() для отладки

List<Integer> numbers = Arrays.asList(5, 2, 8, 1, 9);

List<Integer> result = numbers.stream()
    .filter(n -> n > 2)
    .peek(n -> System.out.println("After filter: " + n))
    .map(n -> n * n)
    .peek(n -> System.out.println("After map: " + n))
    .toList();

// Вывод:
// After filter: 5
// After map: 25
// After filter: 8
// After map: 64
// After filter: 9
// After map: 81

Sorted() для порядка

List<String> names = Arrays.asList("Charlie", "Alice", "Bob");

List<String> sorted = names.stream()
    .sorted()
    .toList();

System.out.println(sorted);  // [Alice, Bob, Charlie]

Комбинация: peek() + sorted()

List<Integer> numbers = Arrays.asList(3, 1, 4, 1, 5);

List<Integer> result = numbers.stream()
    .peek(n -> System.out.println("Before sort: " + n))
    .sorted()
    .peek(n -> System.out.println("After sort: " + n))
    .toList();

// Вывод:
// Before sort: 3
// Before sort: 1
// Before sort: 4
// Before sort: 1
// Before sort: 5
// After sort: 1
// After sort: 1
// After sort: 3
// After sort: 4
// After sort: 5

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

public class Product {
    private String name;
    private double price;
    
    // getters, constructor
}

List<Product> products = Arrays.asList(
    new Product("Laptop", 1200),
    new Product("Mouse", 25),
    new Product("Keyboard", 75)
);

// Сортировка по цене
List<Product> byPrice = products.stream()
    .sorted(Comparator.comparingDouble(Product::getPrice))
    .peek(p -> System.out.println(p.getName() + ": $" + p.getPrice()))
    .toList();

// Вывод:
// Mouse: $25
// Keyboard: $75
// Laptop: $1200

Производительность

Peek() — O(n), не буферизирует

stream.peek(x -> {})  // Просто проходит через каждый элемент

Sorted() — O(n log n), буферизирует всё

stream.sorted()  // Должна получить все элементы, отсортировать, потом отпустить
// На 1 млн элементов
List<Integer> million = IntStream.range(0, 1_000_000).boxed().toList();

// Peek — быстро
long start = System.currentTimeMillis();
million.stream()
    .peek(x -> {})  // O(n)
    .toList();
long peekTime = System.currentTimeMillis() - start;  // ~10ms

// Sorted — медленнее
start = System.currentTimeMillis();
million.stream()
    .sorted()  // O(n log n)
    .toList();
long sortTime = System.currentTimeMillis() - start;  // ~200ms

Антипаттерны

// ❌ Плохо: используем peek() для изменения состояния
struct.peek(item -> item.setProcessed(true));  // Побочный эффект!

// ✅ Хорошо: используем map() для трансформации
struct.map(item -> {
    item.setProcessed(true);
    return item;
});

// ✅ Лучше: просто используй forEachпо необходимости
struct.forEach(item -> item.setProcessed(true));

Ещё про промежуточные операции

// Все промежуточные операции Stream API
stream.filter(predicate)     // Отфильтровать
stream.map(function)          // Трансформировать
stream.flatMap(function)      // Развернуть вложенные стримы
stream.sorted()               // Отсортировать
stream.distinct()             // Удалить дубликаты
stream.limit(n)               // Ограничить количество
stream.skip(n)                // Пропустить n элементов
stream.peek(consumer)         // Отладка (side effect)

Заключение

  • peek() — вспомогательная операция для отладки и логирования, не изменяет Stream, O(n)
  • sorted() — сортирует элементы, изменяет порядок, требует буферизации всех элементов, O(n log n)
  • Обе операции промежуточные (intermediate), но с разными целями
  • peek() для отладки, sorted() для функциональности
  • Не используй peek() для побочных эффектов в production коде