Introduction to Stream
-
Stream is neither a collection nor a data structure. It cannot save data
-
Stream is somewhat similar to the advanced Iterator and can be used for algorithm and calculation
-
Unlike iterators, streams can operate in parallel. Data is divided into many segments and processed in different threads
-
Data source, zero or more intermediate operations, and zero or one terminal operation
-
All intermediate operations are inert and will not have any effect until the pipeline starts working
-
The terminal operation is a bit like a faucet. When the faucet is turned on, the water will flow and the intermediate operation will be executed
1, How to create a street? [Street data source]
The data source is an array
stream = Stream.of("1", "2", "3"); //Equivalent to String[] arr = new String[]{"1", "2", "3"}; Stream<String> stream = Arrays.stream(arr);
Data sources are collections
//Set list, AP set List<String> list = new ArrayList<>(); list.add("I"); list.add("yes"); list.add("aggregate"); Stream<String> stream = list.stream(); //Sequential flow ParallelStream<String> parallelStream =list.parallelStream(); //Parallel flow
Parallel streams can be created through parallelStream(). The default thread pool is ForkJoinPool instead of ThreadPoolTaskExecutor
2, How does Stream flow work? [intermediate operation and terminal operation]
A stream represents a collection of a series of elements that we can perform different types of operations on to perform calculations on these elements. It may sound a little awkward. Let's speak in Code:
List<String> myList = Arrays.asList("a1", "a2", "b1", "c2", "c1"); myList .stream() // Create stream .filter(s -> s.startsWith("c")) // Perform filtering to filter out the string prefixed with c .map(String::toUpperCase) // Convert to uppercase .sorted() // sort .forEach(System.out::println); // for circular printing // C1 // C2
We can perform intermediate operations or terminal operations on convection.
What is intermediate operation? What is terminal operation?
Stream intermediate operation, terminal operation
- ① : the intermediate operation will return a stream again, so we can link multiple intermediate operations. Note that there is no semicolon here. filter filtering, map object conversion and sorted sorting in the above figure belong to intermediate operations.
- ② : terminal operation is an end action of convection operation, which generally returns void or a non stream result. The forEach loop in the figure above is a termination operation.
After reading the above operation, do you feel like a pipeline operation.
In fact, most stream operations support lambda expressions as parameters. Correctly understood, it should be said to accept the implementation of a functional interface as parameters.
3, Different types of Stream streams
1. Distinguish according to execution scheduling
1.1 serial stream
1.2 parallel flow
The thread pool used by parallel streams in Stream is ForkJoinPool, which is characterized by tasks
@Test public void case6(){ List<Person> persons = Arrays.asList( new Person("Max", 18), new Person("Max", 18), new Person("Peter", 23), new Person("Pamela", 23), new Person("David", 12), new Person("David", 12), new Person("David", 12)); //Directly create parallel stream (parallelstream) persons.parallelStream().peek((person)-> System.out.println(person.getName()+":"+Thread.currentThread().getName())).forEach(System.out::println); //First use serial and then parallel persons.stream().distinct().parallel().peek((person)-> System.out.println(person.getName()+":"+Thread.currentThread().getName())).forEach(System.out::println); }
2. Distinguish by data type
2.1 basic data type stream (int,long,double)
- IntStream
- LongStream
- DoubleStream
2.2 object flow
- Stream
4, Intermediate operation
//First define an operation object, and then use List<Person> persons = Arrays.asList( new Person("Max", 18), new Person("Max", 18), new Person("Peter", 23), new Person("Pamela", 23), new Person("David", 12));
1.map
Traversal elements for editing, with return values, can be type converted
@Test public void testMap() { Stream.of("apple", "banana", "orange", "watermelon", "grape") .map(e -> { e = e.substring(1, 1); return e.length(); }) //Length converted to word int .forEach(System.out::println); //output }
2.mapToInt
Object flow basic data flow. Of course, these maps can do it. mapToLong and mapToDouble are similar to mapToInt
@Test public void testMapToInt(){ Stream.of("apple", "banana", "orange", "waltermaleon", "grape") .mapToInt(String::length) //Convert to int .forEach(System.out::println); }
3.peek
Traverse elements for editing and printing information. There is no return value and type conversion is not allowed (in fact, it is provided to us to observe the elements of the flow)
@Test public void tesPeek() { Stream.of( new StringBuilder("apple") ,new StringBuilder("banana"),new StringBuilder("orange"),new StringBuilder("watermelon"),new StringBuilder("grape")) .peek(s-> s.append("aa")) //Splicing aa .map(StringBuilder::toString) .forEach(System.out::println); //output }
4.flatMap
The function of flatMap is to flatten and flatten the elements, reorganize the flattened elements into a Stream, and merge these streams into a Stream in series
//flatMap() parameter, the return value of the functional interface is also a Stream stream <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper); //map(), and the return value of the functional interface is Object <R> Stream<R> map(Function<? super T, ? extends R> mapper);
@Test public void testFlatMap(){ Stream.of("a-b-c-d","e-f-i-g-h") .flatMap(e->Stream.of(e.split("-"))) .forEach(System.out::println); }
5.distinct
De duplication will be judged according to the equals method. If you want to de duplicate your own custom bean s, you need to rewrite the equals method, but this is not the only method. You can also customize the filtering rules
@Test public void testDistinct() { //The Persons object is de duplicated according to equals by default persons.stream() .distinct() .forEach(System.out::println); //Integer de duplication IntStream.of(1, 2, 2, 3) .distinct() .forEach(System.out::println); }
6.Limit
Limit the number of elements
@Test public void testLimit(){ //Persons limit the number of elements persons.stream() .limit(3) .forEach(System.out::println); //Integer limits the number of elements Stream.of(1,2,3,4,5,6) .limit(3) //Limit three .forEach(System.out::println); //The first three 1, 2 and 3 will be output }
7.skip
Skip the first n elements
@Test public void testSkip() { //Persons skips the first n elements persons.stream() .skip(3) .forEach(System.out::println); //Integer skips the first n elements Stream.of(1,2,3,4,5,6) .skip(3) //Limit three .forEach(System.out::println); //The first three 1, 2 and 3 will be output }
8.sorted
The bottom layer of the sorting comparator depends on the Comparable implementation, and a user-defined comparator can also be provided
@Test public void testSorted() { ///People sorting. Note that the sorting rules are customized here persons.stream() .sorted(Comparator.comparingInt(Person::getAge).thenComparing(Person::getName)) .forEach(System.out::println); //Integer sort Stream.of(1,2,4,3,5,6) .sorted() .forEach(System.out::println); }
#Intstream (unique method)
- IntStream
- LongStream
- DoubleStream
1.mapToObj
Basic data flow object flow
@Test public void testMapObject() { // Here, it is converted into a string object and starts with string + a IntStream.of(1, 2, 3) .mapToObj(String::valueOf) .map(s -> "a" + s) .forEach(System.out::println); }
2.boxed
Automatic boxing, converting the basic type int to the object type Integer
@Test public void testBoxed() { // Convert base type to object type IntStream.range(0, 10).boxed().forEach(System.out::println); //amount to IntStream.range(0, 10).mapToObj(Integer::valueOf).forEach(System.out::println); }
5, Terminal operation
//Or the whole collection first List<Person> persons = Arrays.asList( new Person("Max", 18), new Person("Max", 18), new Person("Peter", 23), new Person("Pamela", 23), new Person("David", 12));
1.forEach
forEach is not only the terminal operation of Stream, but also the syntax sugar of collection
@Test public void testForEach() { persons.forEach(System.out::println); }
2.count
Find set length
@Test public void testCount() { long count = persons.stream().count(); System.out.println(count); }
3.findFirst
Gets the first element in the stream
@Test public void testFindFirst() { Optional<Person> optional = persons.stream().findFirst(); Person person = optional.orElse(null); System.out.println(person); }
4.findAny
Get any element in the stream
@Test public void testFindAny() { Optional<Person> optional = persons.stream().findAny(); Person person = optional.orElse(null); System.out.println(person); }
5.max
Find the maximum value and take the last one according to the sorting rules
@Test public void testMax() { Optional<Person> max1 = persons.stream().max((p1, p2) -> { return p1.getAge().compareTo(p2.getAge()); }); Person person1 = max1.orElse(null); System.out.println(person1); //simplify Optional<Person> max2 = persons.stream().max(Comparator.comparing(Person::getAge)); Person person2 = max2.orElse(null); System.out.println(person2); }
6.min
Find the minimum value and take the first one according to the sorting rules
@Test public void testMin() { Optional<Person> min1 = persons.stream().min((p1, p2) -> { return p2.getAge().compareTo(p1.getAge()); }); Person person1 = min1.orElse(null); System.out.println(person1); //simplify Optional<Person> min2 = persons.stream().min(Comparator.comparing(Person::getAge)); Person person2 = min1.orElse(null); System.out.println(person2); }
7.toArray
Turn array
@Test public void testToArray() { Object[] objects = persons.stream().toArray(); Arrays.stream(objects).forEach(System.out::println); }
8.reduce
Merge
The data in the convection calculates a result according to the calculation method you specify(Reduction operation) reduce The function of is to Stream Combine elements in,We can pass in an initial value,It will take the elements in the stream in turn according to our calculation method and calculate on the basis of the basic value,The calculation results are calculated in the following elements
//Find the value of the maximum age //You can use max (actually based on reduce) or reduce //reduce sum, difference set and everything. In fact, it is the continuous calculation of a group of data @Test public void case5(){ List<Person> persons = Arrays.asList( new Person("Max", 18), new Person("Max", 18), new Person("Peter", 23), new Person("Pamela", 23), new Person("David", 12), new Person("David", 12), new Person("David", 12)); //Find the minimum age int minAge = persons.stream().mapToInt(Person::getAge).reduce(Integer.MAX_VALUE,new IntBinaryOperator(){ @Override public int applyAsInt(int left, int right) { return left > right?right:left; } }); System.out.println(minAge); //Find the maximum age, abbreviation int maxAge = persons.stream().mapToInt(Person::getAge).reduce(Integer.MIN_VALUE, Math::max); System.out.println(maxAge); }
9. Collect (most important)
Here is only a brief introduction to the collection function, which is also the most commonly used. Please refer to this article https://www.jianshu.com/p/6ee7e4cd5314
1.toList and toSet
@Test public void testToListSet(){ List<Person> persons = Arrays.asList( new Person("Max", 1), new Person("Max", 2), new Person("Peter", 3), new Person("Pamela", 4), new Person("David", 5)); //toList List<Person> list1 = persons.stream().distinct().collect(Collectors.toList()); list1.forEach(System.out::println); //toSet Set<Person> set = persons.stream().collect(Collectors.toSet()); set.forEach(System.out::println); }
2.toMap
@Test public void testToMap(){ List<Person> persons = Arrays.asList( new Person("Max", 1), new Person("Max", 2), new Person("Peter", 3), new Person("Pamela", 4), new Person("David", 5)); //key value can be the object itself (represented by Function.identity()) or an attribute Map<Person, Person> collect1 = persons.stream().collect(Collectors.toMap(Function.identity(), Function.identity())); collect1.forEach((k,v)->{ System.out.println(k+":"+v); }); Map<Integer, String> collect2 = persons.stream().collect(Collectors.toMap(Person::getAge, Function.identity())); collect2.forEach((k,v)->{ System.out.println(k+":"+v); }); }
3.groupingBy
//There are three overloaded methods //groupingBy(Function) //groupingBy(Function , Collector) //groupingBy(Function, Supplier, Collector) //Function custom grouping rules //Supplier defines the type of Map. The default is HashMap::new //Collector s customize the format of each group. The default is collectors toList() @Test public void testGroup(){ List<Person> persons = Arrays.asList( new Person("Max", 1), new Person("Min", 1), new Person("Peter", 2), new Person("Pamela", 3), new Person("David", 4)); //1. Grouping by age System.out.println("divide into groups according to age"); ConcurrentHashMap<Integer, Set<Person>> map1 = persons.stream().collect(Collectors.groupingBy(Person::getAge, ConcurrentHashMap::new, Collectors.toSet())); map1.forEach((k,v)->{ System.out.println(k+":"+v); }); //2. User defined grouping rules System.out.println("Custom grouping rules"); Map<String, List<Person>> map2 = persons.stream().collect(Collectors.groupingBy((person -> { if (person.getAge().equals(1)){ return "Equal to 1"; } if (person.getAge().equals(2) || person.getAge().equals(3)){ return "Equal to 2 or 3"; } if (person.getAge().equals(4)){ return "Equal to 4"; } return null; } ))); map2.forEach((k,v)->{ System.out.println(k+":"+v); }); //3.partitioningBy partition (can only be divided into two groups) //Judge whether the age is 1 Map<Boolean, List<Person>> map3 = persons.stream().collect(Collectors.partitioningBy(person -> person.getAge().equals(1))); map3.forEach((k,v)->{ System.out.println(k+":"+v); }); }
4.Collectors.joining()
//Splicing strings, there are three overloaded methods //Collectors.joining() //Collectors.joining(CharSequence delimiter) //Collectors.joining(CharSequence delimiter,CharSequence prefix,CharSequence suffix) //Delimiter is a delimiter, which defaults to '' //Prefix is a prefix, and the default is' ' //Suffix is the suffix, and the default is "" @Test public void testJoining(){ List<Person> persons = Arrays.asList( new Person("Max", 1), new Person("Peter", 2), new Person("Pamela", 3), new Person("David", 4)); String s1 = persons.stream().map(Person::getName).collect(Collectors.joining()); String s2 = persons.stream().map(Person::getName).collect(Collectors.joining(",")); String s3 = persons.stream().map(Person::getName).collect(Collectors.joining(",","name:",";")); System.out.println(s1); System.out.println(s2); System.out.println(s3); }
#Intstream (unique method)
- IntStream
- LongStream
- DoubleStream
1.Reduce
Merge operation
//reduce has two overloaded methods //1. The parameters are the starting value and operation rules. The return value is int. there are starting values and operation rules. The two parameters have the same return type and the starting value type. T reduce(T identity, BinaryOperator<T> accumulator) //2. The parameter is an operation rule, and the return value is Optional OptionalInt reduce(IntBinaryOperator op);
@Test public void testReduce() { //There are starting values and operation rules int sum = IntStream.range(0, 10).reduce(0, (v1, v2) -> v1 - v2); System.out.println(sum); // The protocol operation returns optionalInt, which does not necessarily have a value OptionalInt reduce = IntStream.of().reduce((v1, v2) -> v1/v2); reduce.ifPresent(System.out::println); }
2.sum
Sum
@Test public void testSum() { int sum = IntStream.rangeClosed(0, 10).sum(); System.out.println(sum); }
3.average
Average
@Test public void testAverage() { OptionalDouble optionalDouble = IntStream.of(-2, 2, -9, 10, 9).average(); double average = optionalDouble.getAsDouble(); System.out.println(average); }
6, Precautions
- Lazy evaluation (if there is no termination operation, only intermediate operation will not be executed)
- A flow is one-time (once a flow passes through a termination operation, the flow can no longer be used)
- The operation of convection will not affect the original data
Stateless: refers to that the processing of elements is not affected by previous elements;
Stateful: this means that the operation cannot continue until all elements are obtained.
Non short circuit operation: it means that all elements must be processed to obtain the final result;
Short circuit operation: it means that the final result can be obtained when meeting some qualified elements, such as A | B. as long as A is true, there is no need to judge the result of B.
7, Optional
Optional is to solve the bloated problem of null pointer verification
1. Create Optional
Optional.ofNullable(author) is recommended
Optional.of(author) if author==null, null pointer will be reported
public class OptionalDemo { public static void main(String[] args) { //When it is not known whether the author is null, in order to prevent null pointer exceptions Author author = OptionalDemo.getAuthor(); //1. In the past, we did this for nothing if (author != null) { System.out.println(author.getAge()); } //After 2.jdk8, this object can be wrapped as an optional < author > object Optional<Author> optionalAuthor = Optional.ofNullable(author); System.out.println("optionalAuthor:" + optionalAuthor); //Then judge optionalAuthor.ifPresent(author1 -> System.out.println(author1.getName())); //3. Pack the Optional in the method Optional<Author> optionalAuthor2 = OptionalDemo.getOptionalAuthor(); System.out.println("optionalAuthor2:" + optionalAuthor2); optionalAuthor2.ifPresent(author1 -> System.out.println(author1.getName())); } public static Author getAuthor() { Author author = new Author(); author.setAge(18); author.setName("Zhao Yun"); // return null; return author; } public static Optional<Author> getOptionalAuthor() { Author author = new Author(); author.setAge(20); author.setName("Fei Zhang"); author = null; //return Optional.of(author); // The incoming object cannot be empty return Optional.ofNullable(author); } }
2. Safe consumption
ifPresent()
public void ifPresent(Consumer<? super T> consumer) { if (value != null) consumer.accept(value); }
3. Get object
1. Get (not recommended, null object with NoSuchElementException)
Author author1 = optionalAuthor2.get();
2. Orelseget (if the object is null, customize the returned object)
optionalAuthor2.orElseGet(new Supplier<Author>() { @Override public Author get() { Author author2 = new Author(); author2.setAge(18); author2.setName("Cao Cao"); return author2; } });
3. Orelsethrow (throw self-defined exception if the object is null)
optionalAuthor2.orElseThrow(new Supplier() { @Override public Exception get() { return new Exception("holy crap"); } }); //simplify optionalAuthor2.orElseThrow((Supplier<Throwable>) () -> new RuntimeException("holy crap"));
4. Filtration
The filter judges whether the object meets the standard. If it does not, it will be regarded as null
public Optional<T> filter(Predicate<? super T> predicate) { Objects.requireNonNull(predicate); if (!isPresent()) return this; else return predicate.test(value) ? this : empty(); }
5. Judgment
ifPresent determines whether the object is empty. If it is empty, it returns false, and if it is not empty, it returns true
public void ifPresent(Consumer<? super T> consumer) { if (value != null) consumer.accept(value); }
6. Data conversion
map performs type conversion on data, which is the same as that in Stream
public<U> Optional<U> map(Function<? super T, ? extends U> mapper) { Objects.requireNonNull(mapper); if (!isPresent()) return empty(); else { return Optional.ofNullable(mapper.apply(value)); } }
//The return value is also an Optional object Optional<String> optionalName = optionalAuthor2.map(Author::getName);
icate);
if (!isPresent())
return this;
else
return predicate.test(value) ? this : empty();
}
##### 5. Judgment **ifPresent Judge whether the object is empty,Null return false,Not null return true** ```java public void ifPresent(Consumer<? super T> consumer) { if (value != null) consumer.accept(value); }
6. Data conversion
map performs type conversion on data, which is the same as that in Stream
public<U> Optional<U> map(Function<? super T, ? extends U> mapper) { Objects.requireNonNull(mapper); if (!isPresent()) return empty(); else { return Optional.ofNullable(mapper.apply(value)); } }
//The return value is also an Optional object Optional<String> optionalName = optionalAuthor2.map(Author::getName);