Common methods of Java Stream [detailed article]

1. Introduction to Stream API

One of the important features introduced in the Java8 version is a high-level abstraction for processing and operating collections (such as List, Map, Set, etc.). The Stream API provides a more concise and efficient way to process collection data and is more readable. High, especially when filtering, converting, and aggregating data, the operations are simple, convenient and fast.

1.1 Features and advantages
  • Functional style : Stream API uses functional interfaces and Lambda expressions to make code more expressive and concise.
  • Lazy evaluation : Stream operations are usually lazily evaluated, which means that intermediate operations are not executed immediately before the terminal operation, thus improving efficiency.
  • Parallel processing : Parallel processing can be easily implemented through the Stream API, thereby fully utilizing the advantages of multi-core processors and accelerating the process of processing large amounts of data.

The following is an analysis based on some practical application scenarios.

1.2 Two major types
  • Intermediate Operations: Intermediate operations refer to operations performed on a Stream. They return a new Stream, allowing you to perform multiple intermediate operations in a chain.
  • Terminal Operations: Operations that perform final processing on a Stream. When a terminal operation is called, the Stream will begin to perform intermediate operations and generate final results or side effects. The terminal operation is the "trigger" of the Stream. Once the terminal operation is called , the Stream can no longer be used, and intermediate operations can no longer be performed.

2.Stream API intermediate operations

2.1 filter(condition)

Used to filter elements based on specified conditions. It receives a condition as a parameter, retains only elements that meet the condition, and generates a new Stream.
Example:
There is a collection of names, and now we want to filter out people with [big] in their names.

public static void main(String[] args) {
    
    
	List<String> tempList = Arrays.asList("刘一手", "杜子腾", "林大蛋", "Ekko");
	List<String> resList = tempList.stream()
	        .filter(s -> s.contains("大"))
	        .collect(Collectors.toList());
	System.out.println(resList.toString());
}

Output:

[林大蛋]

This Java code shows how to use the Java Stream API to process a list of strings, filter out strings containing the character "big", then collect the filtered results into a new list, and finally output the results.
Let's explain this code step by step:
first we create a collection of four strings, and then use the Stream API to process the tempList.
First, convert tempList into a Stream by calling the stream() method.
Next, use the filter intermediate operation to filter out strings containing the character "big".
The filter method here receives a Lambda expression as a parameter. The Lambda expression s -> s.contains("大")is used to determine whether the string contains the character "big".
Only strings that meet the conditions will be retained in the Stream.
collect(Collectors.toList()) This part is a terminal operation, which collects elements in the Stream into a new list. Here, we use the Collectors.toList() method to collect the filter results in the Stream into a new List and assign it to resList.

2.2 map(function)

Used to perform a mapping operation on each element and convert the element to another type. It receives a Function (mapping function) as a parameter, applies the mapping function to each element, and generates a new Stream. Example: There
is
a A collection of names. Now we need to add the name prefix to all names.

public static void main(String[] args) {
    
    
	List<String> tempList = Arrays.asList("刘一手", "杜子腾", "林大蛋", "Ekko");
	List<String> resList = tempList.stream()
	        .map(s -> "姓名: " + s)
	        .collect(Collectors.toList());
	System.out.println(resList.toString());
}

Output:

[姓名: 刘一手, 姓名: 杜子腾, 姓名: 林大蛋, 姓名: Ekko]

The meaning of this code is to create a name collection, convert the collection into a Stream stream through the stream() method, use the map() method to splice strings for the values ​​​​in each collection, and use the collect() method to collect these elements. to a new List collection and assign it to resList.

The ones here .map(s -> "姓名: " + s)are abbreviations. The detailed and easier-to-understand writing methods are as follows:

.map(s -> { return "Name: " + s; })

2.3 flatMap(function)

Similar to the map operation, but the flatMap operation can map each element into a Stream, and then merge all the generated Streams into a new Stream.

Example:

Create a new static inner class, and then aggregate the collection data in the class

@Data
static class Personnel {
    
    
    // 人员姓名
    private String name;
    // 人员标签
    private List<String> tagList;

    public Personnel(String name, List<String> tagList) {
    
    
        this.name = name;
        this.tagList = tagList;
    }
}

Tips: Now I want to aggregate List<Personnel>the in tagListfor processing, the code is as follows:

public static void main(String[] args) {
    
    
    Personnel personA = new Personnel("张三", Arrays.asList("抽烟", "喝酒", "烫头"));
    Personnel personB = new Personnel("李斯", Arrays.asList("编码", "喝酒", "踢足球"));
    List<Personnel> personnelList = Arrays.asList(personA, personB);
    personnelList.stream()
            .flatMap(p -> p.getTagList().stream())
            .forEach(s -> System.out.print(s + " "));
}

Output:

抽烟 喝酒 烫头 编码 喝酒 踢足球 
2.4 sorted()

Used to sort elements in a Stream. By default, they are sorted in natural order. You can also pass in a custom Comparator to specify sorting rules.

Example:

public class SortedTest {
    
    
    public static void main(String[] args) {
    
    
        List<Integer> numList = Arrays.asList(10, 20, 18, 300, 30, 2);
        // ① 默认排序
        List<Integer> orderList = numList.stream()
                .sorted()
                .collect(Collectors.toList());
        System.out.printf("① 默认排序: %s%n", orderList);
        // ② 自定义排序
        List<Integer> orderDescList = numList.stream()
                .sorted((x, y) -> {
    
    
                    return y.compareTo(x);
                })
                .collect(Collectors.toList());
        System.out.printf("② 自定义排序: %s%n", orderDescList);
    }
}

Output:

① 默认排序: [2, 10, 18, 20, 30, 300]
② 自定义排序: [300, 30, 20, 18, 10, 2]

Regarding return y.compareTo(x);the specific meaning of this section, you can search and compareTofind out the method yourself. I will not go into details here.

2.5 distinct()

Used to remove duplicate elements in the Stream to ensure that each element in the final Stream is unique.

Example:

public class DistinctStreamTest {
    
    
    public static void main(String[] args) {
    
    
        List<Integer> numList = Arrays.asList(1,1,1,1,2,3,2,2);
        List<Integer> distinctList = numList.stream()
                .distinct()
                .collect(Collectors.toList());
        System.out.println(distinctList);
    }
}

Output:

[1, 2, 3]

This code first creates a collection of numbers, and then uses the stream() method to convert it into a Stream. It uses the
distinct() method to deduplicate the elements in the stream, and returns a Stream that does not contain duplicate elements.
Finally, it uses the collect() method to The deduplicated stream is converted into a new List collection.

2.6 limit(long n)

Used to limit the size of the Stream and return a new Stream containing at most the first n elements.

Example:

public class LimitStreamTest {
    
    
    public static void main(String[] args) {
    
    
        List<Integer> numList = Arrays.asList(1,2,3,4,5,6,7,8);
        List<Integer> limitList = numList.stream()
                .limit(4)
                .collect(Collectors.toList());
        System.out.println(limitList);
    }
}

This code creates a List collection containing integers numList, uses the Stream API limit(4)method to intercept the collection into a new List collection containing only the first 4 elements, and outputs the result to the console.

The output result is: [1, 2, 3, 4]

2.7 skip(long n)

Used to skip the first n elements in the Stream and return a new Stream with the remaining elements after discarding the first n elements.

Example:

public class SkipStreamTest {
    
    
    public static void main(String[] args) {
    
    
        List<Integer> numList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);
        List<Integer> skipList = numList.stream()
                .skip(numList.size() - 2)
                .collect(Collectors.toList());
        System.out.println(skipList);
    }
}

This code creates a List collection containing integers numList, uses the Stream API skipmethod to intercept the collection into 集合大小-2a new List collection skipping the previous element, and outputs the result to the console.

The output result is: [7, 8]

2.8 peek(Consumer)

Used to perform an operation on each element while maintaining the flow of the Stream. It can be used for debugging or logging elements in a Stream.

Example:

public class PeekStreamTest {
    
    
    public static void main(String[] args) {
    
    
        List<Integer> numList = Arrays.asList(5, 6, 7, 8);
        List<Integer> resList = numList.stream()
                .peek(System.out::println)
                .filter(s -> s == 5)
                .peek(s -> System.out.printf("过滤后的:%d%n", s))
                .collect(Collectors.toList());
    }
}

Output:

5
过滤后的:5
6
7
8

This code creates a List collection containing integers numList, uses the Stream API peekmethod to record the initial value, filters the ones with a value of 5 through the intermediate operation filter method, and prints and verifies our expression through the peek method again, and then filters the The result generates a new List collection through the collect method.

3. Terminal operation

In the Java Stream API, terminal operations (Terminal Operations) are operations for final processing of Stream. When a terminal operation is called, the Stream begins executing intermediate operations and generates final results or side effects. The terminal operation is a Stream 触发器. Once the terminal operation is called, the Stream can no longer be used, and no intermediate operations can be performed.

The following are some common Stream API terminal operations:

3.1 forEach(Consumer)

Perform the specified operation on each element in the Stream, receiving a Consumer (consumer function) as a parameter. It is usually used to output or perform some operations on the elements in a Stream, but does not return any results.

Example: Traverse and output the collection after intermediate operations

public class ForEachStreamTest {
    
    
    public static void main(String[] args) {
    
    
        // 给公司工资普涨 500
        List<Integer> salaryList = Arrays.asList(12000, 20000, 30000, 4000);
        salaryList.stream()
                .peek(s -> System.out.print("工资普涨前:" + s))
                .map(s -> s + 500)
                .forEach(s -> {
    
    
                    System.out.println("--工资普涨后:" + s);
                });
    }
}

The meaning of the above code is to create a salary collection, convert it into a Stream stream through the stream() method, record the element values ​​before conversion through the intermediate operation peek() method, and then convert the elements through the map() method, and finally use the terminal Operate the forEach() method to traverse.

Output:

工资普涨前:12000--工资普涨后:12500
工资普涨前:20000--工资普涨后:20500
工资普涨前:30000--工资普涨后:30500
工资普涨前:4000--工资普涨后:4500
3.2 collect(Collector)

Used to collect elements in a Stream into a container, receiving a Collector as a parameter. It allows you to perform various collection operations in Stream, such as collecting elements into List, Set, Map and other containers.

Example: Convert User entity collection to Map collection, name as key, salary as Name

public class CollectStreamTest {
    
    
    public static void main(String[] args) {
    
    
        List<User> userList = Arrays.asList(new User("张三", 2000.5),
                new User("李斯", 11000.5),
                new User("王二", 12000.5),
                new User("张六", 32000.5),
                new User("赵公子", 1000000.0));
        Map<String, Double> userSalaryMap = userList.stream()
                .collect(Collectors.toMap(User::getName, User::getSalary));
        userSalaryMap.forEach((k, v) -> {
    
    
            System.out.printf("姓名:%s,工资:%.2f%n", k, v);
        });
    }

    @Data
    @AllArgsConstructor
    static class User {
    
    
        private String name;
        private Double salary;
    }
}

The meaning of the above code is to create a collection of people, convert it into a Stream stream through stream(), use the collect() method to collect the elements, and use Collectors.toMap() to convert the collector to a Map. The internal reception will traverse each element. Collectors.toMap(User::getName, User::getSalary)is an abbreviation, and the detailed writing method is as follows:

Collectors.toMap(s -> s.getName(), s -> s.getSalary())

Output:

姓名:张三,工资:2000.50
姓名:赵公子,工资:1000000.00
姓名:张六,工资:32000.50
姓名:李斯,工资:11000.50
姓名:王二,工资:12000.50
3.3 toArray()

Convert the elements in the Stream to an array. Returns an array containing all elements. The returned array type is automatically inferred based on the type of the stream elements. If the stream is empty, an array of length 0 will be returned.

Example:

public class ToArrayStreamTest {
    
    
    public static void main(String[] args) {
    
    
        // 示例整数流
        IntStream intStream = IntStream.of(1, 2, 3, 4, 5);

        // 使用toArray()将流中的元素收集到一个数组中
        int[] intArray = intStream.toArray();
        
        // 输出结果数组
        System.out.println(Arrays.toString(intArray));
    }
}

Output:

[1, 2, 3, 4, 5]

In this example, we create a stream of integers IntStreamand then use toArray()the method to collect all the integers in the stream into an array and output the resulting array.

3.4 reduce(BinaryOperator)

StreamThe method of the class reduce()is a method used to perform reduction operations on the elements in the stream. Receives one BinaryOperator(二元运算函数as a parameter, operates on two elements, and returns a combined result. It combines all elements in the stream according to the specified rules and returns an Optionalobject, because the stream may be empty.

Example:

public class ReduceStreamTest {
    
    
    public static void main(String[] args) {
    
    
        // 示例整数流
        IntStream intStream = IntStream.of(1, 2, 3, 4, 5);

        // 使用reduce()将流中的整数相加得到总和
        OptionalInt sumOptional = intStream.reduce((a, b) -> a + b);

        // 获取结果总和,如果流为空,则给出一个默认值0
        int sum = sumOptional.orElse(0);

        // 输出结果总和
        System.out.println("总和: " + sum);
    }
}

Output:

总和: 15

In the above code, we create a stream of integers IntStreamand then use reduce()the method to add the integers in the stream to get the sum. Since there are elements in the stream, reduce()the method returns an object containing the sum Optional. We use to orElse(0)get the sum of the results to prevent the stream from being empty. Finally, the sum of the results is output.

3.5 min(Comparator) / max(Comparator)

Streammin()The and methods of the class max()are terminal operations for finding the minimum and maximum values ​​in a stream. They accept an Comparatorobject as argument to determine the order of elements, and return an Optionalobject because the stream may be empty.

Here is a brief explanation of min()the and max()methods along with example code:

min()method:

Optional<T> min(Comparator<? super T> comparator)
  • Method signature:<T> Optional<T> min(Comparator<? super T> comparator)
  • min()Method used to find the smallest element in a stream.
  • comparatorParameters are used to determine the order of elements so that the minimum value is found.
  • If the stream is empty, an empty Optionalobject is returned.

max()method:

Optional<T> max(Comparator<? super T> comparator)
  • Method signature:<T> Optional<T> max(Comparator<? super T> comparator)
  • max()Method used to find the largest element in a stream.
  • comparatorParameters are used to determine the order of elements so that the maximum value is found.
  • If the stream is empty, an empty Optionalobject is returned.

Example: Suppose we have a stream containing integers and we want to find the minimum and maximum values ​​in it

public class MinMaxStreamTest {
    
    
    public static void main(String[] args) {
    
    
        // 示例整数流
        Stream<Integer> integerStream = Stream.of(1, 5, 3, 8, 2);

        // 使用min()找到最小值
        Optional<Integer> minOptional = integerStream.min(Integer::compareTo);
        if (minOptional.isPresent()) {
    
    
            System.out.println("最小值为: " + minOptional.get());
        } else {
    
    
            System.out.println("流为空.");
        }

        // 重新创建一个整数流,因为流已被消耗
        Stream<Integer> newIntegerStream = Stream.of(1, 5, 3, 8, 2);

        // 使用max()找到最大值
        Optional<Integer> maxOptional = newIntegerStream.max(Integer::compareTo);
        if (maxOptional.isPresent()) {
    
    
            System.out.println("最大值为: " + maxOptional.get());
        } else {
    
    
            System.out.println("流为空.");
        }
    }
}

Output:

最小值为: 1
最大值为: 8

In the above code, we create a stream of integers Stream<Integer>, then use min()the method to find the minimum value, and max()the method to find the maximum value. We use Optionala object to handle the possible null case and output the minimum and maximum values ​​found. Note that once the stream is consumed, it cannot be used again, so we recreated a stream of integers to find the maximum after finding the minimum.

3.6 count()

StreamThe method of the class count()is a terminal operation used to count the number of elements in the stream. It returns a longvalue of type representing the number of elements in the stream. count()The method is a terminal operation and once the method is called, the stream is consumed and cannot be used again.

Example: We have a stream containing integers and we want to count the number of elements in the stream

public class CountStreamTest {
    
    
    public static void main(String[] args) {
    
    
        // 示例整数流
        Stream<Integer> integerStream = Stream.of(1, 5, 3, 8, 2);

        // 使用count()计算流中的元素个数
        long count = integerStream.count();

        // 输出元素个数
        System.out.println("元素数量: " + count);
    }
}

Output:

元素数量: 5

In the above code, we create a stream of integers Stream<Integer>and then use count()the method to count the number of elements in the stream. Since this is a terminal operation, once the method is called count(), the stream is consumed and cannot be used again. We output the calculated number of elements.

3.7 anyMatch(Predicate) / allMatch(Predicate) / noneMatch(Predicate)

StreamClasses anyMatch(), allMatch(), and noneMatch()are terminal operations used to check whether an element in a stream satisfies a specific condition. They return a Boolean value indicating whether an element in the stream meets a specified condition. These methods may terminate stream processing prematurely after encountering an element that satisfies the condition. anyMatch checks whether any element satisfies the condition, allMatch checks whether all elements satisfy the condition, and noneMatch checks whether no element satisfies the condition.

Here is a brief explanation of anyMatch()the , allMatch(), and noneMatch()methods along with example code:

anyMatch()method:

boolean anyMatch(Predicate<? super T> predicate)
  • Method signature:boolean anyMatch(Predicate<? super T> predicate)
  • anyMatch()Method used to check whether there is at least one element in the stream that meets the given condition.
  • It accepts a Predicateparameter to define the judgment rule that satisfies the condition.
  • Returns if at least one element in the stream satisfies the condition, trueotherwise returns false.

allMatch()method:

boolean allMatch(Predicate<? super T> predicate)
  • Method signature:boolean allMatch(Predicate<? super T> predicate)
  • allMatch()Method is used to check whether all elements in the stream meet the given conditions.
  • It accepts a Predicateparameter to define the judgment rule that satisfies the condition.
  • Returns if all elements in the stream satisfy the condition, trueotherwise returns false.

noneMatch()method:

boolean noneMatch(Predicate<? super T> predicate)
  • Method signature:boolean noneMatch(Predicate<? super T> predicate)
  • noneMatch()Method used to check whether all elements in the stream do not meet the given condition.
  • It accepts a Predicateparameter to define the judgment rule that satisfies the condition.
  • Returns if all elements in the stream do not meet the condition, trueotherwise returns false.

Sample code:

Suppose we have a stream containing integers, and we want to check if there are certain elements in the stream that satisfy a certain condition.

public class MatchStreamTest {
    
    

    public static void main(String[] args) {
    
    
        // 示例整数流
        Stream<Integer> integerStream = Stream.of(1, 5, 3, 8, 2);

        // 使用anyMatch()检查是否存在元素大于5
        boolean anyGreaterThan5 = integerStream.anyMatch(num -> num > 4);
        System.out.println("是否存在元素大于 5 ?" + anyGreaterThan5);

        // 重新创建一个整数流,因为流已被消耗
        Stream<Integer> newIntegerStream = Stream.of(1, 5, 3, 8, 2);

        // 使用allMatch()检查是否所有元素都小于10
        boolean allLessThan10 = newIntegerStream.allMatch(num -> num < 10);
        System.out.println("所有元素都小于10 ? " + allLessThan10);

        // 重新创建一个整数流,因为流已被消耗
        Stream<Integer> newestIntegerStream = Stream.of(1, 5, 3, 8, 2);

        // 使用noneMatch()检查是否没有元素等于10
        boolean noneEqualTo10 = newestIntegerStream.noneMatch(num -> num == 10);
        System.out.println("是否没有元素等于 10 ? " + noneEqualTo10);
    }
}

Output:

是否存在元素大于 5 ?true
所有元素都小于10? true
是否没有元素等于 10 ? true

In the above code, we create a stream of integers Stream<Integer>and then use anyMatch()the method to check if there is an element greater than 5, the method allMatch()to check if all elements are less than 10, and noneMatch()the method to check if no element is equal to 10. We output the results of the check. Note that after each use of the terminal operation, the stream is consumed and cannot be used again, so the stream needs to be recreated when checking different conditions.

3.8 findFirst() / findAny()

StreamfindFirst()The and methods of the class findAny()are used for terminal operations that find elements in the stream. They both return an Optionalobject representing the found element or the likelihood of the element. In a parallel stream, findAny()the method may be faster since it doesn't necessarily have to iterate over all elements. In serial Stream, findFirst()and findAny()return the same element, in parallel Stream, findAny()the first element found is returned.

Here is a brief explanation of findFirst()the and findAny()methods along with example code:

findFirst()method:

Optional<T> findFirst()
  • Method signature:Optional<T> findFirst()
  • findFirst()Method is used to find the first element in the stream that satisfies the condition.
  • It returns an Optionalobject representing the found element, or an empty Optionalobject if the stream is empty.

findAny()method:

Optional<T> findAny()
  • Method signature:Optional<T> findAny()
  • findAny()Method is used to find any element in the stream that satisfies the condition.
  • It returns an Optionalobject representing the found element, or an empty Optionalobject if the stream is empty.
  • In parallel streams, findAny()the method may be findFirst()faster than the method because it does not necessarily need to iterate over all elements.

Sample code:

Suppose we have a stream containing integers and we want to find a certain element in it.

public class FindStreamTest {
    
    
    public static void main(String[] args) {
    
    
        // 示例整数流
        Stream<Integer> integerStream = Stream.of(1, 5, 3, 8, 2);

        // 使用findFirst()找到第一个元素
        Optional<Integer> firstElementOptional = integerStream.findFirst();
        if (firstElementOptional.isPresent()) {
    
    
            System.out.println("发现第一个元素: " + firstElementOptional.get());
        } else {
    
    
            System.out.println("流为空!");
        }

        // 重新创建一个整数流,因为流已被消耗
        Stream<Integer> newIntegerStream = Stream.of(1, 5, 3, 8, 2);

        // 使用findAny()找到任意一个元素
        Optional<Integer> anyElementOptional = newIntegerStream.findAny();
        if (anyElementOptional.isPresent()) {
    
    
            System.out.println("找到任意一个元素: " + anyElementOptional.get());
        } else {
    
    
            System.out.println("流为空!");
        }
    }
}

Output (note that the output results may vary due to the different processing order of the streams):

发现第一个元素: 1
找到任意一个元素: 1

In the above code, an integer stream is created Stream<Integer>, then findFirst()the method is used to find the first element, findAny()the method is used to find any element, and the found element is output. Note that once these methods are called, the stream is consumed and cannot be used again. In parallel streams, findAny()the method may be findFirst()faster than the method because it does not necessarily need to iterate over all elements.

Guess you like

Origin blog.csdn.net/Fine_Cui/article/details/131913537