Ограничение потока в Java 8: аналог функции takeWhile
Быстрый ответ
Чтобы улучшить производительность потоков в Java, используйте метод takeWhile
. Он прерывает поток, как только обнаруживается значение, не соответствующее условию предиката:
List<String> filtered = Stream.of("first", "second", "halt", "third")
.takeWhile(s -> !"halt".equals(s))
.collect(Collectors.toList());
// Результат: ["first", "second"]
Метод takeWhile
можно рассматривать как надежный механизм слежения за выполнением потока.
Обходное решение для Java 8
Реализация takeWhile
до выпуска JDK 9
В Java 8 можно самостоятельно применить функциональность, аналогичную takeWhile
, используя Spliterator
и StreamSupport
:
public static <T> Stream<T> takeWhile(Stream<T> stream, Predicate<T> predicate) {
Spliterator<T> splitr = stream.spliterator();
return StreamSupport.stream(
new Spliterators.AbstractSpliterator<T>(splitr.estimateSize(), 0) {
boolean running = true; // Начальное состояние: без перерывов
@Override
public boolean tryAdvance(Consumer<? super T> action) {
if (running) {
boolean hasMore = splitr.tryAdvance(elem -> {
if (predicate.test(elem)) {
action.accept(elem);
} else {
running = false; // Переходим в состояние перерыва
}
});
return hasMore && running;
}
return false;
}
}, false); // Параллельные потоки не предусматривают перерывы
}
StreamEx приходит с помощью
Библиотека StreamEx предоставляет собственный метод takeWhile
, воссоздающий функциональность, внедренную в JDK 9:
List<String> list = StreamEx.of("one", "two", "stop", "three")
.takeWhile(s -> !"stop".equals(s))
.toList();
// Результат: ["one", "two"]
StreamEx обеспечивает отличную производительность, опираясь на MethodHandle.invokeExact
.
Проект Protonpack не остается в стороне
Библиотека Protonpack включает функцию takeWhile
, доступную для Java 8:
List<String> result = StreamUtils.takeWhile(Stream.of("1", "2", "stop", "3"), s -> !s.equals("stop"))
.collect(Collectors.toList());
// Результат: ["1", "2"]
Обе библиотеки предоставляют возможность использования takeWhile
в Java 8.
Использование IntStream с предикатом
С помощью метода iterate
класса IntStream, добавив peek
и allMatch
, можно контролировать потоки следующим образом:
int[] nums = IntStream.iterate(1, i -> i + 1)
.peek(i -> { if (i > 5) throw new BreakerException(); }) // Неожиданный выход
.limit(10)
.toArray();
Внимание: Использование BreakerException
для прекращения работы может привести к проблемам с обработкой исключений.
Визуализация
Наглядно Java Stream представляет собой конвейер:
Конвейер (🏭): [🔩, 📦, 🔧, 🔨, 🧰]
Predicate
выступает в роли инспектора по контролю качества 🕵️♂️:
stream.filter(item -> inspectorLikes(item)); // Инспектор выбирает подходящие элементы
Как только встречается первый неподходящий элемент, Stream API прекращает обработку последующих:
Инспекция: [🔩✅, 📦✅, 🔧❌, 🔨⛔, 🧰⛔] // Как бы сказал Саймон Кауэлл: "Все, кончено"
Поток замирает, как автомат с напитками, сломанный в неопожеланный момент.
Важность эффективности
Сравнение Java 8 и Java 9+
Несмотря на то что пользовательский takeWhile
для Java 8 кажется применимым, его исполнение занимает больше времени, чем работа с встроенным методом в Java 9 и выше.
Места для усовершенствований
Чтобы воспользоваться встроенными и оптимизированными возможностями, рекомендуется перейти на JDK 9 или более поздние версии.
StreamEx: Возможностей больше, чем кажется
StreamEx предлагает не только takeWhile
. Стоит обратить внимание на его документацию — это ценный источник информации о потоках, который заслуживает изучения.
Полезные материалы
- Stream (Java Platform SE 8 ) — Документация Java 8 по потокам.
- Processing Data with Java SE 8 Streams, Part 1 — Руководство Oracle по работе с потоками данных в Java 8.
- java – Limit a stream by a predicate – Stack Overflow — Решения сообщества по ограничению потока с использованием предиката.
- Java 8 – Streams – Основы обработки потоков в Java 8.
- Open source Java projects: Vert.x – Пример использования Java на примере проекта Vert.x.