1. Stream features
Stream is a new interface in Java 8. Stream can be thought of as an advanced version of Iterator. It represents a flow of data in which the number of data elements can be either finite or infinite.
The difference between Stream and Iterator is
- Storage-free: A Stream is a data source-based object that does not store data elements itself, but rather pipes elements from the data source to the operation.
- Functional programming: Any changes to a Stream do not change the data source behind it. For example, performing a filter operation on a Stream does not remove the filtered elements, but instead creates a new Stream that does not contain the filtered elements.
- Delayed execution: A Stream operation consists of zero or more intermediate operations and one terminal operation. The intermediate operations defined by a Stream are not executed sequentially until the end operation is performed, which is the delayed nature of a Stream.
- Consumability: A Stream can only be “consumed” once and becomes invalid once traversed. As with container iterators, a new Stream must be generated in order to iterate again.
Ii. New functional interfaces in Java 8
The operation of Stream is based on a combination of functional interfaces. The new functional interfaces in Java8 are in the java.util.function package. These functional interfaces can be classified in a number of ways.
2.1 the Function
Function is a unary mapping Function from T to R. Pass the argument T to a function that returns R. R = Function (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(a) {
returnt -> t; }}Copy the code
Function implements three default methods: compose, andThen, and identity.
The method name | The corresponding function | describe |
---|---|---|
compose | V=Function(ParamFunction(T)) | It represents a nested relationship |
andThen | V= ParamFunction(Function(T)) | Converts the order of nesting |
identity | Function(T)=T | Pass its own function call |
Compose and andThen for the two functions f and g, f.compose(g) is equivalent to g.pose (f).
2.2 Predicate
Predicate is a Predicate function, which returns a Boolean value as a Predicate calculus that deduces true or false values. Predicate is equivalent to a subset of the Boolean return value of Function.
@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 {@codeother} * 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(a) {
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 {@codeother} * 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); }}Copy the code
The default methods for Predicate are AND, negate, or.
2.3 Consumer
Consumer is an unary function from T to void that takes an input but returns no result.
@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); }; }}Copy the code
The default method for Consumer is andThen.
2.4: Supplier
An Supplier is a Supplier representing the result.
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get(a);
}
Copy the code
Use of Supplier:
Supplier<String> supplier = new Supplier<String>() {
@Override
public String get(a) {
return "hello suppiler"; }}; System.out.println(supplier.get());Copy the code
Or:
Supplier<User> userSupplier = User::new;
userSupplier.get(); // new User
Copy the code
Java 8 added CompletableFuture, which uses Supplier as an input to many of its methods.
3. Stream
3.1 Stream creation
Java 8 has several ways to create a Stream:
- Through the stream() method of the collection or parallelStream()
- Use a static method for the Stream, such as stream.of (Object[]), intstream.range (int, int), or stream.iterate (Object, UnaryOperator).
- Through the arrays.stream (Object[]) method.
- Bufferedreader.lines () gets a stream of lines from a file.
- Methods of the Files class that operate on paths, such as list, find, walk, etc.
- Random number stream random.ints ().
- Other classes provide methods for creating streams, such as bitset.stream (), Pattern.splitasstream (java.lang.charsequence), and jarfile.stream ().
Ultimately, you rely on the underlying StreamSupport class to create the Stream.
3.2 Intermediate Operations
Intermediate operations can be Stateless or Stateful. A Stateless intermediate operation refers to the operation which was independent of the previous elements, while a Stateful intermediate operation does not know the final result until all elements are processed.
The intermediate operation of the Stream is just a token, and the actual calculation is triggered only when the end operation is performed. Students familiar with RxJava and Scala can see that various methods of Stream intermediate operation can be found in RxJava and Scala.
3.3 Ending Operations
3.3.1 Short circuit Operation
Short-circuiting means that the result is returned without having to deal with all the elements. The short-circuit operation must be handled one element at a time.
3.3.1 Non-short-circuit operation
Non-short-circuiting operations can process data in batches, but require processing of all elements before returning results.
Iv. Parallel flow
When creating a Stream, the default is to create a serial Stream. However, you can use parallelStream() to create a parallel stream or parallel() to convert a serial stream into a parallel stream. Parallel streams can also be converted to serial streams via sequential().
Java 8 Stream’s parallel streams, essentially, use the Fork/Join model.
In live.
In Java development, if you are using Java 8, you are strongly advised to use Stream. Because each operation of Stream can rely on a Lambda expression, it is a declarative way of handling data, and Stream improves data processing and development efficiency.