1. Особенности потока
Stream — это новый интерфейс в Java 8, и Stream можно рассматривать как расширенную версию Iterator. Он представляет собой поток данных, и количество элементов данных в потоке может быть ограничено или неограничено.
Разница между потоком и итератором
- Нет хранилища: Stream — это объект, основанный на источнике данных, он не хранит элементы данных сам по себе, а передает элементы источника данных операциям через конвейер.
- Функциональное программирование: любое изменение потока не изменит базовый источник данных.Например, выполнение операции фильтрации в потоке не приведет к удалению отфильтрованных элементов, но создаст новый поток, который не содержит отфильтрованных элементов.
- Отложенное выполнение: Потоковые операции состоят из нуля или более промежуточных операций и терминальной операции. Только после выполнения конечной операции промежуточные операции, определенные Stream, будут выполняться последовательно, что является характеристикой задержки Stream.
- Расходуемость: поток можно «использовать» только один раз, и он станет недействительным после его прохождения. Так же, как итератор контейнера, новый поток должен быть регенерирован для повторного прохождения.
2. Новый функциональный интерфейс Java 8
Потоковые операции построены на композиции функциональных интерфейсов. Все новые функциональные интерфейсы в Java8 находятся в пакете java.util.function. Эти функциональные интерфейсы можно классифицировать по-разному.
2.1 Function
Функция — это унарная функция отображения из T в R. Передайте аргумент T функции, которая возвращает R. т. е. R = функция (T)
@FunctionalInterface
public interface Function<T, R> {
/**
* Applies this function to the given argument.
*
* @param t the function argument
* @return the function result
*/
R apply(T t);
/**
* Returns a composed function that first applies the {@code before}
* function to its input, and then applies this function to the result.
* If evaluation of either function throws an exception, it is relayed to
* the caller of the composed function.
*
* @param <V> the type of input to the {@code before} function, and to the
* composed function
* @param before the function to apply before this function is applied
* @return a composed function that first applies the {@code before}
* function and then applies this function
* @throws NullPointerException if before is null
*
* @see #andThen(Function)
*/
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
/**
* Returns a composed function that first applies this function to
* its input, and then applies the {@code after} function to the result.
* If evaluation of either function throws an exception, it is relayed to
* the caller of the composed function.
*
* @param <V> the type of output of the {@code after} function, and of the
* composed function
* @param after the function to apply after this function is applied
* @return a composed function that first applies this function and then
* applies the {@code after} function
* @throws NullPointerException if after is null
*
* @see #compose(Function)
*/
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
/**
* Returns a function that always returns its input argument.
*
* @param <T> the type of the input and output objects to the function
* @return a function that always returns its input argument
*/
static <T> Function<T, T> identity() {
return t -> t;
}
}
Функция по умолчанию реализует три метода по умолчанию, а именно compose, andThen и identity.
имя метода | соответствующая функция | описывать |
---|---|---|
compose | V=Function(ParamFunction(T)) | Он воплощает вложенные отношения |
andThen | V= ParamFunction(Function(T)) | Порядок вложенности изменен |
identity | Function(T)=T | Вызов функции, который проходит сам себя |
compose и andThen Для двух функций f и g функция f.compose(g) эквивалентна g.andThen(f).
2.2 Predicate
Предикат — это функция предиката, которая в основном используется в качестве исчисления предикатов для вывода о существовании истинных и ложных значений и возврата функции логических значений. Предикат эквивалентен подмножеству логического возвращаемого значения функции.
@FunctionalInterface
public interface Predicate<T> {
/**
* Evaluates this predicate on the given argument.
*
* @param t the input argument
* @return {@code true} if the input argument matches the predicate,
* otherwise {@code false}
*/
boolean test(T t);
/**
* Returns a composed predicate that represents a short-circuiting logical
* AND of this predicate and another. When evaluating the composed
* predicate, if this predicate is {@code false}, then the {@code other}
* predicate is not evaluated.
*
* <p>Any exceptions thrown during evaluation of either predicate are relayed
* to the caller; if evaluation of this predicate throws an exception, the
* {@code other} predicate will not be evaluated.
*
* @param other a predicate that will be logically-ANDed with this
* predicate
* @return a composed predicate that represents the short-circuiting logical
* AND of this predicate and the {@code other} predicate
* @throws NullPointerException if other is null
*/
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
/**
* Returns a predicate that represents the logical negation of this
* predicate.
*
* @return a predicate that represents the logical negation of this
* predicate
*/
default Predicate<T> negate() {
return (t) -> !test(t);
}
/**
* Returns a composed predicate that represents a short-circuiting logical
* OR of this predicate and another. When evaluating the composed
* predicate, if this predicate is {@code true}, then the {@code other}
* predicate is not evaluated.
*
* <p>Any exceptions thrown during evaluation of either predicate are relayed
* to the caller; if evaluation of this predicate throws an exception, the
* {@code other} predicate will not be evaluated.
*
* @param other a predicate that will be logically-ORed with this
* predicate
* @return a composed predicate that represents the short-circuiting logical
* OR of this predicate and the {@code other} predicate
* @throws NullPointerException if other is null
*/
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
/**
* Returns a predicate that tests if two arguments are equal according
* to {@link Objects#equals(Object, Object)}.
*
* @param <T> the type of arguments to the predicate
* @param targetRef the object reference with which to compare for equality,
* which may be {@code null}
* @return a predicate that tests if two arguments are equal according
* to {@link Objects#equals(Object, Object)}
*/
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
Методы Predicate по умолчанию: и, отрицание или.
2.3 Consumer
Consumer — это унарная функция от T до void, которая принимает один вход и не возвращает результата.
@FunctionalInterface
public interface Consumer<T> {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
/**
* Returns a composed {@code Consumer} that performs, in sequence, this
* operation followed by the {@code after} operation. If performing either
* operation throws an exception, it is relayed to the caller of the
* composed operation. If performing this operation throws an exception,
* the {@code after} operation will not be performed.
*
* @param after the operation to perform after this operation
* @return a composed {@code Consumer} that performs in sequence this
* operation followed by the {@code after} operation
* @throws NullPointerException if {@code after} is null
*/
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
Метод Consumer по умолчанию — andThen.
2.4 Supplier
Поставщик – это поставщик, который представляет результат.
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
Использование поставщика:
Supplier<String> supplier = new Supplier<String>() {
@Override
public String get() {
return "hello suppiler";
}
};
System.out.println(supplier.get());
или:
Supplier<User> userSupplier = User::new;
userSupplier.get(); // new User
В Java 8 добавлен CompletableFuture, и многие из его методов используют Supplier.
3. Использование потока
3.1 Создание потока
В Java 8 есть несколько способов создания потоков:
- Через метод коллекции stream() или parallelStream()
- Используйте статические методы потоков, такие как Stream.of(Object[]), IntStream.range(int, int) или Stream.iterate(Object, UnaryOperator).
- Через метод Arrays.stream(Object[]).
- BufferedReader.lines() получает поток строк из файла.
- Методы работы с путем класса Files, такие как list, find, walk и т. д.
- Поток случайных чисел Random.ints().
- Другие классы предоставляют методы для создания потоков, такие как BitSet.stream(), Pattern.splitAsStream(java.lang.CharSequence) и JarFile.stream().
На самом деле, в конце концов, он полагается на базовый класс StreamSupport для завершения создания Stream.
3.2 Промежуточные операции
Промежуточные операции можно разделить на без состояния (Stateless) и с состоянием (Stateful).Промежуточные операции без состояния означают, что на обработку элементов не влияют предыдущие элементы, тогда как промежуточные операции с состоянием должны ждать, пока не будут обработаны все элементы.Конечный результат.
Промежуточная операция Stream — это своего рода метка, и фактическое вычисление будет запущено только при выполнении конечной операции. Учащиеся, знакомые с RxJava и Scala, могут видеть, что различные методы промежуточных операций с потоками можно найти знакомыми в RxJava и Scala.
3.3 Завершение операции
3.3.1 Работа при коротком замыкании
Операция короткого замыкания — это операция, которая возвращает результат без обработки всех элементов. Операции короткого замыкания должны обрабатываться по одному элементу за раз.
3.3.1 Работа без короткого замыкания
Операции без короткого замыкания могут обрабатывать данные пакетами, но должны обрабатывать все элементы, прежде чем возвращать результат.
4. Параллельный поток
При создании потока по умолчанию создается последовательный поток. Но вы можете использовать parallelStream() для создания параллельного потока или parallel() для преобразования последовательного потока в параллельный поток. Параллельные потоки также могут быть преобразованы в последовательные потоки с помощью sequence().
Параллельный поток Java 8 Stream по существу использует модель Fork/Join.
V. Резюме
При разработке Java, если используется Java 8, настоятельно рекомендуется Stream. Поскольку каждая операция Stream может полагаться на лямбда-выражения, это декларативный метод обработки данных, а Stream повышает эффективность обработки и разработки данных.