Detailed usage of Stream

1 Definition of stream

A stream is a sequence of elements generated from a source that supports data processing operations. The source can be an array, file, collection, or function. A stream is not a collection element, it is not a data structure and does not hold data, its main purpose is computation.

2 ways to generate streams

2.1 Generation by collection

List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> stream = integerList.stream();

2.2 Generation by array

int[] intArr = new int[]{
    
    1, 2, 3, 4, 5};
IntStream stream = Arrays.stream(intArr);

The stream is generated by the Arrays.stream method, and the stream generated by this method is a numerical stream [that is, IntStream] instead of a Stream.

Using numeric streams can avoid unboxing and boxing during calculations and improve performance.

The Stream API provides mapToInt, mapToDouble, and mapToLong three ways to convert the object stream [i.e. Stream] into the corresponding numerical stream.

At the same time, the boxed method is provided to convert the numerical stream into an object stream.

2.3 Generation by value

Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);

Generate a stream through the of method of Stream, and generate an empty stream through the empty method of Stream

2.4 Generated from files

Stream<String> lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset());

Get a stream through the Files.line method, and each stream obtained is a line in the given file

2.5 Generation by function

Provides two static methods iterate and generate to generate streams from functions

2.5.1 iterate

Stream<Integer> stream = Stream.iterate(0, n -> n + 2).limit(5);

The iterate method accepts two parameters, the first is the initialization value, and the second is the function operation to be performed, because the flow generated by the iterator is an infinite flow, the flow is truncated by the limit method, and only 5 even numbers are generated

2.5.2 generate

Stream<Double> stream = Stream.generate(Math::random).limit(5);

The generate method accepts one parameter, the method parameter type is Supplier, which provides the value for the stream. The stream generated by generate is also an infinite stream, so the stream is truncated by limit.

3 Operation types of streams

3.1 Intermediate operations

A stream can be followed by zero or more intermediate operations. Its purpose is mainly to open the stream, do some level of data mapping/filtering, and then return a new stream for use by the next operation. This kind of operation is inert. Just calling this kind of method does not actually start the traversal of the stream. The real traversal needs to wait until the terminal operation. Common intermediate operations include filter, map, etc. that will be introduced below.

3.2 Terminal Operation

A stream has and can only have one terminal operation. When this operation is executed, the stream is closed and cannot be operated any more. Therefore, a stream can only be traversed once. If you want to traverse it, you need to generate the stream through the source data. The execution of the terminal operation will actually start the traversal of the stream. Such as count, collect, etc. to be introduced below.

4 Use of Streams

4.1 Intermediate operations

4.1.1 filter screening

List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5);
Stream<Integer> stream = integerList.stream().filter(i -> i > 3);

By using the filter method for conditional filtering, the method parameter of the filter is a condition

4.1.2 distinct removes duplicate elements

List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5);
Stream<Integer> stream = integerList.stream().distinct();

Quickly remove duplicate elements through the distinct method

4.1.3 limit returns the number of specified streams

List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5); 
Stream<Integer> stream = integerList.stream().limit(3);

Use the limit method to specify the number of returned streams. The parameter value of limit must be >=0, otherwise an exception will be thrown

4.1.4 skip skips elements in the stream

List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5);
Stream<Integer> stream = integerList.stream().skip(2);

Use the skip method to skip the elements in the stream. The above example skips the first two elements, so the printed results are 2, 3, 4, 5. The parameter value of skip must be >=0, otherwise an exception will be thrown

4.1.5 map stream mapping

The so-called stream mapping is to map the received element into another element.

List<String> stringList = Arrays.asList("Java 8", "Lambdas",  "In", "Action");
Stream<Integer> stream = stringList.stream().map(String::length);

The mapping can be completed through the map method. This example completes the mapping of String -> Integer

4.1.6 flatMap stream conversion

Converts every value in one stream to another.

List<String> wordList = Arrays.asList("Hello", "World");
List<String> strList = wordList.stream()
    .map(w -> w.split(""))
    .flatMap(Arrays::stream)
    .distinct()
    .collect(Collectors.toList());

The return value of map(w -> w.split("")) is Stream<String[]>, we want to get Stream, and we can use the flatMap method to complete the conversion of Stream -> Stream

4.1.7 Element matching

1. allMatch matches all

List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
if (integerList.stream().allMatch(i -> i > 3)) {
    
    
    System.out.println("值都大于3");
}

2. anyMatch matches one of them

List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
if (integerList.stream().anyMatch(i -> i > 3)) {
    
    
    System.out.println("存在大于3的值");
}

3. NoneMatch does not match all

List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
if (integerList.stream().noneMatch(i -> i > 3)) {
    
    
    System.out.println("值都小于3");
}

4.2 Terminal Operation

4.2.1 Count the number of elements in the stream

1. By counting

List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Long result = integerList.stream().count();

2. Through counting

List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Long result = integerList.stream().collect(counting());

Especially useful when used in conjunction with collect

4.2.2 Search

1. findFirst finds the first one

List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> result = integerList.stream().filter(i -> i > 3).findFirst();

Use the findFirst method to find the first element greater than three and print it.

2. findAny randomly finds a

List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> result = integerList.stream().filter(i -> i > 3).findAny();

Use the findAny method to find one of the elements greater than three and print it. Because of internal optimization, it will end when the first element greater than three is found. The result of this method is the same as the result of the findFirst method. The findAny method is provided to make better use of parallel streams, and the findFirst method has more restrictions on parallelism

4.2.3 reduce combines the elements in the stream

Suppose we sum the values ​​in a collection

Before jdk8

int sum = 0;
for (int i : integerList) {
    
    
	sum += i;
}

After jdk8, it is processed by reduce

int sum = integerList.stream().reduce(0, (a, b) -> (a + b));

It can be done in one line, and can also be shortened using method references:

int sum = integerList.stream().reduce(0, Integer::sum);

reduce accepts two parameters, an initial value here is 0, and a BinaryOperator accumulator to combine the two elements to produce a new value,

In addition, the reduce method also has an overloaded method with no initialization value.

4.2.4 Get the minimum and maximum values ​​in the stream

1. Obtain the minimum and maximum values ​​through min/max

Optional<Integer> min = menu.stream().map(Dish::getCalories).min(Integer::compareTo);
Optional<Integer> max = menu.stream().map(Dish::getCalories).max(Integer::compareTo);

can also be written as

OptionalInt min = menu.stream().mapToInt(Dish::getCalories).min();
OptionalInt max = menu.stream().mapToInt(Dish::getCalories).max();

min gets the minimum value in the stream, max gets the maximum value in the stream, and the method parameter is Comparator<? super T> comparator.

2. Obtain the minimum and maximum values ​​through minBy/maxBy

Optional<Integer> min = menu.stream().map(Dish::getCalories).collect(minBy(Integer::compareTo));
Optional<Integer> max = menu.stream().map(Dish::getCalories).collect(maxBy(Integer::compareTo));

minBy gets the minimum value in the stream, maxBy gets the maximum value in the stream, and the method parameter is Comparator<? super T> comparator.

3. Obtain the minimum and maximum values ​​through reduce

Optional<Integer> min = menu.stream().map(Dish::getCalories).reduce(Integer::min);
Optional<Integer> max = menu.stream().map(Dish::getCalories).reduce(Integer::max);

4.2.5 Summing

1. Through summingInt

int sum = menu.stream().collect(summingInt(Dish::getCalories));

If the data type is double or long, the summingDouble and summingLong methods are used for summing.

2. Through reduce

int sum = menu.stream().map(Dish::getCalories).reduce(0, Integer::sum);

3. By sum

int sum = menu.stream().mapToInt(Dish::getCalories).sum();

When summing, calculating the maximum value and the minimum value above, there are different methods for the same operation to choose to perform. You can choose collect, reduce, and min/max/sum methods, and min, max, and sum methods are recommended. Because it is the most concise and easy to read, and at the same time converts the object stream into a numerical stream through mapToInt, avoiding boxing and unboxing operations.

4.2.6 Calculate the average value by averagingInt

double average = menu.stream().collect(averagingInt(Dish::getCalories));

If the data type is double or long, the averaging is performed by averagingDouble and averagingLong methods.

4.2.7 Simultaneously calculate the sum, average, maximum and minimum values ​​by summarizingInt

IntSummaryStatistics intSummaryStatistics = menu.stream().collect(summarizingInt(Dish::getCalories));
double average = intSummaryStatistics.getAverage();  //获取平均值
int min = intSummaryStatistics.getMin();  //获取最小值
int max = intSummaryStatistics.getMax();  //获取最大值
long sum = intSummaryStatistics.getSum();  //获取总和

If the data type is double or long, use the summarizingDouble and summarizingLong methods.

4.2.8 Element traversal through foreach

List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
integerList.stream().forEach(System.out::println);

4.2.9 Returning collections

List<String> strings = menu.stream().map(Dish::getName).collect(Collectors.toList());
Set<String> sets = menu.stream().map(Dish::getName).collect(Collectors.toSet());

Through the use of traversing and returning collections, it is found that the flow only puts the original external iteration inside, which is also one of the main characteristics of the flow. Internal iteration can reduce a lot of code

4.2.10 Joining the elements in the stream by joining

String result = menu.stream().map(Dish::getName).collect(Collectors.joining(", "));

By default, if the map method is not used to map the string returned by the splicing toString method, the joining method parameter is the delimiter of the element. If it is not specified, the generated string will be a string, which is not very readable.

4.2.11 Advanced grouping by groupingBy

Map<Type, List<Dish>> result = dishList.stream().collect(Collectors.groupingBy(Dish::getType));

In the collect method, groupingBy is passed in for grouping, where the method parameter of groupingBy is a classification function. It is also possible to use groupingBy for multi-level classification by nesting.

Map<String, Map<Integer, List<Dish>>> result = menu.stream().collect(Collectors.groupingBy(Dish::getName,
                Collectors.groupingBy(Dish::getCalories)));

4.2.12 Advanced partitioning by partitioningBy

Partition is a special grouping, and its classification is based on true and false, so the returned results can be divided into two groups at most

Map<Boolean, List<Dish>> result = menu.stream().collect(Collectors.partitioningBy(Dish::isVegetarian));

Equivalent to

Map<Boolean, List<Dish>> result = menu.stream().collect(Collectors.groupingBy(Dish::isVegetarian));

This example may not be able to see the difference between partition and classification, and even think that partition is not necessary at all. Let's change to a more obvious example:

List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Map<Boolean, List<Integer>> result = integerList.stream().collect(Collectors.partitioningBy(i -> i < 3));

The key of the return value is still of boolean type, but its classification is sorted by range, and partitioning is more suitable for handling sorting by range.

5 Comprehensive case

5.1 List <Bond> bonds转成Map<PrimaryKey, List<String>>

PrimaryKey is a key of Bond (don't simply understand it as the primary key). It has two fields. Use this key to find the corresponding List<Bond>, and then extract the bondName in List<Bond> and put it in the List through conversion. Stored as a Map<PrimaryKey, List<String>> structure

It is assumed that the fields of Bond have the following

@Data
public class Bond {
    
    
    private Integer bondId;
    
    private String securityCode;
    
    private Integer marketNo;
    
    private String bondName;
    
    private BigDecimal price;
}
Map<PrimaryKey, List<String>> groupMap = bonds.stream().collect(Collectors.groupingBy(bond -> new PrimaryKey(bond.getSecurityCode(), bond.getMarketNo())))
      .entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().stream().map(Bond::getBondName).distinct().collect(Collectors.toList())));

Je suppose que tu aimes

Origine blog.csdn.net/qq_41821963/article/details/125364126
conseillé
Classement