1
头图

concept

Stream set of elements to be processed as a kind of flow. During the flow, Stream API , such as filtering, sorting, and aggregation.

image-20210701194245361

Stream are roughly divided into two types: intermediate operators and termination operators

Intermediate operator

For data flow, the data flow can still be passed to the next level of operators after the intermediate operator executes the specified processing program.

intermediate operators include 8 types (parallel, sequential are excluded, these two operations do not involve processing operations on the data stream):

  1. map(mapToInt,mapToLong,mapToDouble) conversion operator, such as A->B, here is provided by default the operator to convert int, long, double.
  2. flatmap(flatmapToInt,flatmapToLong,flatmapToDouble) Flattening operations such as turning int[]{2,3,4} flat to 2, 3, 4, which means that the original one data becomes three data, which are provided by default here It is the operators of int, long, double.
  3. Limit current limit operation, for example, if there are 10 data streams, I can use only the first 3 ones.
  4. The distint deduplication operation, deduplication of repeated elements, uses the equals method at the bottom.
  5. filter filter operation, filter unwanted data.
  6. Peek picks out operations, if you want to perform certain operations on the data, such as reading, editing, and so on.
  7. skip Skip the operation, skip some elements.
  8. sorted(unordered) Sorting operation, sorting elements, the premise is to implement the Comparable interface, of course, you can also customize the comparator.

Termination operator

After the intermediate processing operation, it is the turn of the termination operator to play;

termination operator is used to collect or consume data. When the data reaches the termination operation, it will not flow downward. The termination operator can only be used once.

  1. The collect collection operation collects all data. This operation is very important. The official Collectors provides a lot of collectors. It can be said that the core of Stream lies in Collectors.
  2. count Statistics operation, count the final number of data.
  3. FindFirst, findAny search operations, find the first one, find any one of them, and return the type as Optional.
  4. For noneMatch, allMatch, anyMatch matching operations, whether there is a qualified element in the data stream, the return value is a bool value.
  5. Min and max maximum value operations require a custom comparator to return the maximum and minimum values in the data stream.
  6. The reduce specification operation reduces the value of the entire data stream to a value. The bottom layer of count, min, and max uses reduce.
  7. ForEach, forEachOrdered traversal operations, here is the consumption of the final data.
  8. toArray array operation, convert the elements of the data stream into an array.

Stream creation

1. Create a stream with a collection through the java.util.Collection.stream()

List<String> list = Arrays.asList("a", "b", "c");
// 创建一个顺序流
Stream<String> stream = list.stream();
// 创建一个并行流
Stream<String> parallelStream = list.parallelStream();

2. Use the java.util.Arrays.stream(T[] array) method to create a stream with an array

int[] array={1,3,5,6,8};
IntStream stream = Arrays.stream(array);

3, using Stream static methods: of()、iterate()、generate()

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

Stream<Integer> stream2 = Stream.iterate(0, (x) -> x + 3).limit(4);
stream2.forEach(System.out::println); // 0 3 6 9

Stream<Double> stream3 = Stream.generate(Math::random).limit(3);
stream3.forEach(System.out::println);

Output result:

3
6
9
0.8106623442686114
0.11554643727388458
0.1404645961428974

Process finished with exit code 0
The simple distinction between stream and parallelStream

stream is a sequential stream, and the main thread performs operations on the stream in sequence;
parallelStream is a parallel stream. The stream is operated in parallel by multiple threads, but the premise is that there is no order requirement for data processing in the stream.

For example, to filter odd numbers in the collection, the handling of the two is different:

image-20210701230623951

Stream use

Traverse/match (foreach/find/match)

Stream also supports similar traversal and match elements of the collection, but Stream the elements are Optional existing types. The traversal and matching of Stream

public class StreamTest {

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(7, 6, 9, 3, 8, 2, 1);
        // 遍历输出符合条件的元素
        list.stream().filter(x -> x > 6).forEach(System.out::println);
        // 匹配第一个
        Optional<Integer> findFirst = list.stream().filter(x -> x > 6).findFirst();
        // 匹配任意(适用于并行流)
        Optional<Integer> findAny = list.parallelStream().filter(x -> x > 6).findAny();
        // 是否包含符合特定条件的元素
        boolean anyMatch = list.stream().anyMatch(x -> x < 6);
        System.out.println("匹配第一个值:" + findFirst.get());
        System.out.println("匹配任意一个值:" + findAny.get());
        System.out.println("是否存在大于6的值:" + anyMatch);
        
    }
}

Output result:

7
9
8
匹配第一个值:7
匹配任意一个值:8
是否存在大于6的值:true

Process finished with exit code 0

Filter

Filtering is the operation of checking the elements in the stream according to certain rules, and extracting the elements that meet the conditions into the new stream.

filters out Integer set, and prints it out

public class StreamTest {

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(6, 7, 3, 8, 1, 2, 9);
        Stream<Integer> stream = list.stream();
        stream.filter(x -> x > 7).forEach(System.out::println);
    }
}

Output result:

8
9

Process finished with exit code 0

Aggregation (max/min/count)

max , min , count , yes, we often use them for data statistics in mysql. These concepts and usages are also introduced in Java stream, which greatly facilitates our data statistics work on collections and arrays.

Case 1: Get the longest element in the String

public class StreamTest {

    public static void main(String[] args) {
        List<String> list = Arrays.asList("adnm", "admmt", "pot", "xbangd", "weoujgsd");
        Optional<String> max = list.stream().max(Comparator.comparing(String::length));
        System.out.println("最长的字符串:" + max.get());
    }
}

Output result:

最长的字符串:weoujgsd

Process finished with exit code 0

Case 2: Get the maximum value in the Integer

public class StreamTest {

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(7, 6, 9, 4, 11, 6);
        // 自然排序
        Optional<Integer> max = list.stream().max(Integer::compareTo);
        // 自定义排序
        Optional<Integer> max2 = list.stream().max(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1.compareTo(o2);
            }
        });
        System.out.println("自然排序的最大值:" + max.get());
        System.out.println("自定义排序的最大值:" + max2.get());
    }
}

Output result:

自然排序的最大值:11
自定义排序的最大值:11

Process finished with exit code 0

Case 3: Calculate the number of elements greater than 6 in the Integer

public class StreamTest {

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(7, 6, 4, 8, 2, 11, 9);
        long count = list.stream().filter(x -> x > 6).count();
        System.out.println("list中大于6的元素个数:" + count);
    }
}

Output result:

list中大于6的元素个数:4

Process finished with exit code 0

Map (map/flatMap)

Mapping can map elements of one stream to another stream according to certain mapping rules. Divided into map and flatMap :

  • map : Receive a function as a parameter, the function will be applied to each element, and map it into a new element.
  • flatMap : Receive a function as a parameter, replace each value in the stream with another stream, and then connect all streams into one stream.

Case 1: The elements of the English string array are all changed to uppercase. Each element of the integer array is +3.

public class StreamTest {

    public static void main(String[] args) {
        String[] strArr = { "abcd", "bcdd", "defde", "fTr" };
        List<String> strList = Arrays.stream(strArr).map(String::toUpperCase).collect(Collectors.toList());
        System.out.println("每个元素大写:" + strList);
        List<Integer> intList = Arrays.asList(1, 3, 5, 7, 9, 11);
        List<Integer> intListNew = intList.stream().map(x -> x + 3).collect(Collectors.toList());
        System.out.println("每个元素+3:" + intListNew);
    }
}

Output result:

每个元素大写:[ABCD, BCDD, DEFDE, FTR]
每个元素+3:[4, 6, 8, 10, 12, 14]

Process finished with exit code 0

Case 2: Combine two character arrays into a new character array.

public class StreamTest {

    public static void main(String[] args) {
        List<String> list = Arrays.asList("m,k,l,a", "1,3,5,7");
        List<String> listNew = list.stream().flatMap(s -> {
            // 将每个元素转换成一个stream
            String[] split = s.split(",");
            Stream<String> s2 = Arrays.stream(split);
            return s2;
        }).collect(Collectors.toList());
        System.out.println("处理前的集合:" + list);
        System.out.println("处理后的集合:" + listNew);
    }
}

Output result:

处理前的集合:[m,k,l,a, 1,3,5,7]
处理后的集合:[m, k, l, a, 1, 3, 5, 7]

Process finished with exit code 0

Reduce

Reduction, also called reduction, as the name suggests, is to reduce a stream to a value, which can realize the operations of summing, product and maximization of the set.

Case 1: Find the sum, product and maximum value of the elements in the Integer

public class StreamTest {

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 3, 2, 8, 11, 4);
        // 求和方式1
        Optional<Integer> sum = list.stream().reduce(Integer::sum);
        // 求和方式2
        Optional<Integer> sum2 = list.stream().reduce(Integer::sum);
        // 求和方式3
        Integer sum3 = list.stream().reduce(0, Integer::sum);
        // 求乘积
        Optional<Integer> product = list.stream().reduce((x, y) -> x * y);
        // 求最大值方式1
        Optional<Integer> max = list.stream().reduce((x, y) -> x > y ? x : y);
        // 求最大值写法2
        Integer max2 = list.stream().reduce(1, Integer::max);
        System.out.println("list求和:" + sum.get() + "," + sum2.get() + "," + sum3);
        System.out.println("list求积:" + product.get());
        System.out.println("list求和:" + max.get() + "," + max2);
    }
}

Output result:

list求和:29,29,29
list求积:2112
list求和:11,11

Process finished with exit code 0

Collection (toList/toSet/toMap)

Because the stream does not store data, after the data in the stream is processed, the data in the stream needs to be reassembled into a new collection. toList , toSet and toMap more commonly used, and there are more complicated toConcurrentMap toCollection and 060e721090b617.

Let's use a case to demonstrate toList , toSet and toMap :

public class Person {

    private String name;  // 姓名
    private int salary; // 薪资
    private int age; // 年龄
    private String sex; //性别
    private String area;  // 地区

    // 构造方法
    public Person(String name, int salary, int age,String sex,String area) {
        this.name = name;
        this.salary = salary;
        this.age = age;
        this.sex = sex;
        this.area = area;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getSalary() {
        return salary;
    }

    public void setSalary(int salary) {
        this.salary = salary;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getArea() {
        return area;
    }

    public void setArea(String area) {
        this.area = area;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", salary=" + salary +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                ", area='" + area + '\'' +
                '}';
    }
}
public class StreamTest {

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 6, 3, 4, 6, 7, 9, 6, 20);
        List<Integer> listNew = list.stream().filter(x -> x % 2 == 0).collect(Collectors.toList());
        Set<Integer> set = list.stream().filter(x -> x % 2 == 0).collect(Collectors.toSet());
        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Tom", 8900, 23, "male", "New York"));
        personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
        personList.add(new Person("Anni", 8200, 24, "female", "New York"));
        Map<?, Person> map = personList.stream().filter(p -> p.getSalary() > 8000)
                .collect(Collectors.toMap(Person::getName, p -> p));
        System.out.println("toList:" + listNew);
        System.out.println("toSet:" + set);
        System.out.println("toMap:" + map);
    }
}

Output result:

toList:[6, 4, 6, 6, 20]
toSet:[4, 20, 6]
toMap:{Tom=Person{name='Tom', salary=8900, age=23, sex='male', area='New York'}, Anni=Person{name='Anni', salary=8200, age=24, sex='female', area='New York'}}

Process finished with exit code 0

Statistics (count/averaging)

Collectors provides a series of static methods for data statistics:

  • Count: count
  • Average: averagingInt , averagingLong , averagingDouble
  • The most value: maxBy , minBy
  • Sum: summingInt , summingLong , summingDouble
  • Statistics of all the above: summarizingInt , summarizingLong , summarizingDouble

case: Count the number of employees, average wages, total wages, and maximum wages.

public class StreamTest {

    public static void main(String[] args) {
        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Tom", 8900, 23, "male", "New York"));
        personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
        // 求总数
        long count = personList.size();
        // 求平均工资
        Double average = personList.stream().collect(Collectors.averagingDouble(Person::getSalary));
        // 求最高工资
        Optional<Integer> max = personList.stream().map(Person::getSalary).max(Integer::compare);
        // 求工资之和
        int sum = personList.stream().mapToInt(Person::getSalary).sum();
        // 一次性统计所有信息
        DoubleSummaryStatistics collect = personList.stream().collect(Collectors.summarizingDouble(Person::getSalary));
        System.out.println("员工总数:" + count);
        System.out.println("员工平均工资:" + average);
        System.out.println("员工最高工资:" + max.get());
        System.out.println("员工工资总和:" + sum);
        System.out.println("员工工资所有统计:" + collect);
    }
}

Output result:

员工总数:3
员工平均工资:7900.0
员工最高工资:8900
员工工资总和:23700
员工工资所有统计:DoubleSummaryStatistics{count=3, sum=23700.000000, min=7000.000000, average=7900.000000, max=8900.000000}

Process finished with exit code 0

Grouping (partitioningBy/groupingBy)

  • Zoning: The stream conditionally divided into two Map , such as employee salaries is higher than 8000 by divided into two parts.
  • Grouping: Divide the collection into multiple Maps, such as grouping employees by gender. There are single-level grouping and multi-level grouping.

case: Divide employees into two parts according to whether their salary is higher than 8000; group employees according to gender and region

public class StreamTest {

    public static void main(String[] args) {
        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Tom", 8900, 23, "male", "Washington"));
        personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 21, "female", "New York"));
        personList.add(new Person("Anni", 8200, 24, "female", "New York"));
        // 将员工按薪资是否高于8000分组
        Map<Boolean, List<Person>> part = personList.stream().collect(Collectors.partitioningBy(x -> x.getSalary() > 8000));
        // 将员工按性别分组
        Map<String, List<Person>> group = personList.stream().collect(Collectors.groupingBy(Person::getSex));
        // 将员工先按性别分组,再按地区分组
        Map<String, Map<String, List<Person>>> group2 = personList.stream().collect(Collectors.groupingBy(Person::getSex, Collectors.groupingBy(Person::getArea)));
        System.out.println("员工按薪资是否大于8000分组情况:" + part);
        System.out.println("员工按性别分组情况:" + group);
        System.out.println("员工按性别、地区:" + group2);
    }
}

Output result:

员工按薪资是否大于8000分组情况:{false=[Person{name='Jack', salary=7000, age=25, sex='male', area='Washington'}, Person{name='Lily', salary=7800, age=21, sex='female', area='New York'}], true=[Person{name='Tom', salary=8900, age=23, sex='male', area='Washington'}, Person{name='Anni', salary=8200, age=24, sex='female', area='New York'}]}
员工按性别分组情况:{female=[Person{name='Lily', salary=7800, age=21, sex='female', area='New York'}, Person{name='Anni', salary=8200, age=24, sex='female', area='New York'}], male=[Person{name='Tom', salary=8900, age=23, sex='male', area='Washington'}, Person{name='Jack', salary=7000, age=25, sex='male', area='Washington'}]}
员工按性别、地区:{female={New York=[Person{name='Lily', salary=7800, age=21, sex='female', area='New York'}, Person{name='Anni', salary=8200, age=24, sex='female', area='New York'}]}, male={Washington=[Person{name='Tom', salary=8900, age=23, sex='male', area='Washington'}, Person{name='Jack', salary=7000, age=25, sex='male', area='Washington'}]}}

Process finished with exit code 0

Joining

joining can connect the elements in the stream with a specific connector (if not, connect directly) into a string.

public class StreamTest {

    public static void main(String[] args) {
        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Tom", 8900, 23, "male", "New York"));
        personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
        String names = personList.stream().map(Person::getName).collect(Collectors.joining(","));
        System.out.println("所有员工的姓名:" + names);
        List<String> list = Arrays.asList("A", "B", "C");
        String string = list.stream().collect(Collectors.joining("-"));
        System.out.println("拼接后的字符串:" + string);
    }
}

Output result:

所有员工的姓名:Tom,Jack,Lily
拼接后的字符串:A-B-C

Process finished with exit code 0

Sorted

sorted , intermediate operation. There are two sorts:

  • sorted() : Natural sorting, elements in the stream need to implement the Comparable interface
  • sorted(Comparator com) : Comparator custom sort

case: sort employees according to salary from high to low (the same salary is from

public class StreamTest {

    public static void main(String[] args) {
        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Sherry", 9000, 24, "female", "New York"));
        personList.add(new Person("Tom", 8900, 22, "male", "Washington"));
        personList.add(new Person("Jack", 9000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 8800, 26, "male", "New York"));
        personList.add(new Person("Alisa", 9000, 26, "female", "New York"));
        // 按工资升序排序(自然排序)
        List<String> newList = personList.stream().sorted(Comparator.comparing(Person::getSalary)).map(Person::getName)
                .collect(Collectors.toList());
        // 按工资倒序排序
        List<String> newList2 = personList.stream().sorted(Comparator.comparing(Person::getSalary).reversed())
                .map(Person::getName).collect(Collectors.toList());
        // 先按工资再按年龄升序排序
        List<String> newList3 = personList.stream()
                .sorted(Comparator.comparing(Person::getSalary).thenComparing(Person::getAge)).map(Person::getName)
                .collect(Collectors.toList());
        // 先按工资再按年龄自定义排序(降序)
        List<String> newList4 = personList.stream().sorted((p1, p2) -> {
            if (p1.getSalary() == p2.getSalary()) {
                return p2.getAge() - p1.getAge();
            } else {
                return p2.getSalary() - p1.getSalary();
            }
        }).map(Person::getName).collect(Collectors.toList());
        System.out.println("按工资升序排序:" + newList);
        System.out.println("按工资降序排序:" + newList2);
        System.out.println("先按工资再按年龄升序排序:" + newList3);
        System.out.println("先按工资再按年龄自定义降序排序:" + newList4);
    }
}

Output result:

按工资升序排序:[Lily, Tom, Sherry, Jack, Alisa]
按工资降序排序:[Sherry, Jack, Alisa, Tom, Lily]
先按工资再按年龄升序排序:[Lily, Tom, Sherry, Jack, Alisa]
先按工资再按年龄自定义降序排序:[Alisa, Jack, Sherry, Tom, Lily]

Process finished with exit code 0

Extraction/Combination

Streams can also be merged, deduplicated, restricted, skipped, etc.

public class StreamTest {

    public static void main(String[] args) {
        String[] arr1 = { "a", "b", "c", "d" };
        String[] arr2 = { "d", "e", "f", "g" };
        Stream<String> stream1 = Stream.of(arr1);
        Stream<String> stream2 = Stream.of(arr2);
        // concat:合并两个流 distinct:去重
        List<String> newList = Stream.concat(stream1, stream2).distinct().collect(Collectors.toList());
        // limit:限制从流中获得前n个数据
        List<Integer> collect = Stream.iterate(1, x -> x + 2).limit(10).collect(Collectors.toList());
        // skip:跳过前n个数据
        List<Integer> collect2 = Stream.iterate(1, x -> x + 2).skip(1).limit(5).collect(Collectors.toList());
        System.out.println("流合并:" + newList);
        System.out.println("limit:" + collect);
        System.out.println("skip:" + collect2);
    }
}

Output result:

流合并:[a, b, c, d, e, f, g]
limit:[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
skip:[3, 5, 7, 9, 11]

Process finished with exit code 0

Paging operation

The power of stream api is not only to perform various combined operations on collections, but also to support paging operations.

For example, to sort the following array from small to large, after the sorting is completed, starting from the first row, query 10 pieces of data, the operation is as follows:

//需要查询的数据
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5, 10, 6, 20, 30, 40, 50, 60, 100);
List<Integer> dataList = numbers.stream().sorted(Integer::compareTo).skip(0).limit(10).collect(Collectors.toList());
System.out.println(dataList.toString());

Output result:

[2, 2, 3, 3, 3, 5, 6, 7, 10, 20]

Process finished with exit code 0

Parallel operation

The so-called parallel means that multiple tasks occur at the same point in time and are processed by different CPUs, and do not grab resources from each other; while concurrency means that multiple tasks occur at the same time at the same time, but by the same The cpu processes and grabs resources from each other.

There is only one difference between parallel operation and serial operation of stream api, and the others are the same. For example, below we use parallelStream to output the number of empty strings:

List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd", "", "jkl");
// 采用并行计算方法,获取空字符串的数量
long count = strings.parallelStream().filter(String::isEmpty).count();
System.out.println(count);

is actually used, parallel operation is not necessarily faster than serial operation! For simple operations, the number is very large, and if the server is multi-core, it is recommended to use Stream Parallel! On the contrary, serial operation is more reliable!

Set to Map operation

In the actual development process, one of the most frequently used operations is to use a primary key field in the collection element as the key and the element as the value to achieve the requirement of converting the collection to the map. This requirement is very used in data assembly. many.

public static void main(String[] args) {
    List<Person> personList = new ArrayList<>();
    personList.add(new Person("Tom",7000,25,"male","安徽"));
    personList.add(new Person("Jack",8000,30,"female","北京"));
    personList.add(new Person("Lucy",9000,40,"male","上海"));
    personList.add(new Person("Airs",10000,40,"female","深圳"));
    Map<Integer, Person> collect = personList.stream().collect(Collectors.toMap(Person::getAge, v -> v, (k1, k2) -> k1));
    System.out.println(collect);
}

Output result:

{40=Person{name='Lucy', salary=9000, age=40, sex='male', area='上海'}, 25=Person{name='Tom', salary=7000, age=25, sex='male', area='安徽'}, 30=Person{name='Jack', salary=8000, age=30, sex='female', area='北京'}}

Process finished with exit code 0

Open the Collectors.toMap method and take a look.

public static <T, K, U>
    Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
                                    Function<? super T, ? extends U> valueMapper,
                                    BinaryOperator<U> mergeFunction) {
    return toMap(keyMapper, valueMapper, mergeFunction, HashMap::new);
}

It can be seen from the parameter table:

  • The first parameter: represents the key
  • The second parameter: represents value
  • The third parameter: indicates a certain rule

The above Collectors.toMap(Person::getAge, v -> v, (k1,k2) -> k1) , is to express the meaning age content as key , v -> v is an element person as value , wherein (k1,k2) -> k1 means that if the same is present key , the first content as a matching element, the second discard!

end

This article mainly focuses on jdk stream api operations, combined with actual daily development needs, and made a simple summary and sharing. I hope you can follow along and get a deeper impression. I believe you can master the preliminary usage of these operators; I will take you step by step in the follow-up articles. After reading it, I hope you can like it, haha.


初念初恋
175 声望17 粉丝