JDK8新特性Stream流

1. Stream流的出现原因

我们操作集合的时候有时候十分麻烦,如下


JDK8新特性Stream流_第1张图片

JDK8新特性Stream流_第2张图片
image.png

上面的集合操作数据的时候,每一个需求都需要一个新的集合来存放数据,十分麻烦。
为此我们引入Stream流式操作。
注意:Stream和IO流没有任何关系。

2. Stream流式思想概述

Stream流式思想类似与工厂车间的“生产流水线”。Stream流不是一种数据结构,不保存数据,而是对数据进行加工处理,Stream可以看作式流水线上的一个工序,在流水线上,通过多个工序让一个原材料加工成一个产品。


JDK8新特性Stream流_第3张图片
image.png
JDK8新特性Stream流_第4张图片
image.png

看到Stream的filter和map方法参数,都是一个接口,可以使用lambda表达式。
使用方式如下:

public class Demo01Intro {
    public static void main(String[] args) {
        List list = new ArrayList<>();
        Collections.addAll(list, "张无忌", "周芷若", "赵敏", "张强", "张三丰");

        list.stream().filter((s) -> {
            System.out.println("s1" + s);
            return s.startsWith("张");   // 返回姓张的
        }).filter((s)->{
            System.out.println("s2" +s);
            return s.length() == 3;   // 返回长3的
        }).forEach((s)->{
            System.out.println(s);
        });

    }

且我们运行结果这样的:


JDK8新特性Stream流_第5张图片
image.png

3. 获取流的两种方式

  • 根据Collection获取Stream
  • Stream中的静态方法of获取流
public class Demo02GetStream {
    public static void main(String[] args){
        // 1. 根据Collection获取流
        // list继承Collection
        List list = new ArrayList<>();
        Stream stream1 = list.stream();

        // Set获取流
        HashSet set = new HashSet<>();
        Stream stream2 = set.stream();

        // 根据Map的key和value获取流
        Map map = new HashMap<>();
        Stream stream3 = map.keySet().stream();
        Stream stream4 = map.values().stream();
        Stream> stream5 = map.entrySet().stream();

        // 2. 根据Stream的of静态方法
        // public static Stream of(T... values)
        Stream stream6 = Stream.of("a", "b", "c");

        String[] strs = {"aa","bb","cc"};
        Stream stream7 = Stream.of(strs);
        // 基本数据类型的数据的Stream流会把整个数组 int[] 作为一个值传进流中,
        // 而不能操作数组里面的数据
        int[] arr = {1,2,3};
        Stream arr1 = Stream.of(arr);
    }
}

4. Stream的注意事项

1. Stream只能操作一次

JDK8新特性Stream流_第6张图片
image.png

2. Stream方法返回的是新的流

JDK8新特性Stream流_第7张图片
image.png

3. Stream方法不调用终结方法,中间的操作不会执行

JDK8新特性Stream流_第8张图片
image.png

5. Stream的常用方法

JDK8新特性Stream流_第9张图片
image.png
  • 终结方法:返回值类型不再是Stream类型的方法,不在支持链式调用。包括count,forEach方法
  • 非终结方法(函数拼接方法):返回值类型仍然式Stream类型的方法,支持链式调用。

5.1 forEach

遍历留里面的所有元素进行操作


JDK8新特性Stream流_第10张图片
image.png

5.2 count

统计流里面元素的个数

long count = list.stream().count();
System.out.println(count);

5.3 filter

用户过滤数据,返回符合过滤条件的数据

list.stream().filter((String str) -> {
            return str.length() == 3;
        }).forEach(s -> System.out.println(s));

5.4 limit

对流进行截取,只取前n个

list.stream().limit(3).forEach(s -> System.out.println(s));

5.5 skip

和limit相反,对流进行截取,跳过前n个获取后面的

list.stream().skip(3).forEach(s -> System.out.println(s));

5.6 map

将流中的元素映射到另一个流中去。将一种类型的流转换成另一种类型的流


JDK8新特性Stream流_第11张图片
map
        // String类型的流变成Integer类型
        Stream original = Stream.of("1", "2", "3");
        Stream integerStream = original.map(s -> {
            return Integer.parseInt(s);
        });
        integerStream.forEach(s->{
            System.out.println(s);
        });

5.7 sorted

可以对数据进行排序,是一个函数拼接方法。

// sorted排序
 original.map(s -> {
            return Integer.parseInt(s);
        }).sorted().forEach(System.out::println);

original.map(s -> {
            return Integer.parseInt(s);
        }).sorted((Integer i1, Integer i2)->{
            return i2 - i1;
        }).forEach(System.out::println);

5.8 distinct

如果要去除重复元素,可以使用distinct方法。是一个函数拼接方法。


JDK8新特性Stream流_第12张图片
image.png
Stream stream = Stream.of(1, 2, 5, 4, 3, 4, 3, 5, 2);
stream.distinct().forEach(System.out::println);

注意对于自定义类型的数据,无法直接去重,需要我们重写equals和hashcode方法


JDK8新特性Stream流_第13张图片
image.png

5.9 match

如果要判断数据是否匹配指定的条件,可以使用match方法。是一个终结方法。
一共有三个

boolean anyMatch(Predicate predicate);
boolean allMatch(Predicate predicate);
boolean noneMatch(Predicate predicate);
JDK8新特性Stream流_第14张图片
image.png

5.10 find

如果需要寻找某些数据,可以使用find方法

    Optional findFirst();
    Optional findAny();
Optional first = stream.findFirst();
        Integer integer = first.get();
        System.out.println(integer);

5.11 max和min

获取最大和最小值

Optional max = stream.max((o1, o2) -> o1 - o2);
System.out.println("最大值" + max.get());

Optional min = stream.min((o1, o2) -> o1 - o2);
System.out.println("最小值" + min.get());

5.12 reduce

如果需要将所有数据归纳得到一个数据,可以使用reduce方法。

T reduce(T identity, BinaryOperator accumulator);
T identity:默认值
BinaryOperator accumulator:对数据进行处理的方式
JDK8新特性Stream流_第15张图片
image.png

JDK8新特性Stream流_第16张图片
image.png

JDK8新特性Stream流_第17张图片
image.png
Integer reduce = stream.reduce(0, (x, y) -> {
    System.out.println("x = " + x +", y = " + y);
    return x + y;
});
System.out.println(reduce);

        // 获取最大值
Integer reduce = stream.reduce(0, (x, y) -> {
    return x > y ? x : y;
});
System.out.println(reduce);

5.13 map和reduce的组合使用

通过map的转换,获取到我们需要的数据的流,然后通过reduce的计算,将我们需要的数据给返回。


JDK8新特性Stream流_第18张图片
image.png
JDK8新特性Stream流_第19张图片
image.png
JDK8新特性Stream流_第20张图片
image.png

5.13 mapToInt

如果需要将Stream中的Integer转换成int
类型,可以使用mapToInt。
Integer占用的内存比int多,再Stream流操作中,会自动装箱和拆箱。

        // IntStream内部操作的是int类型,可以节省内存,减少自动装箱
        IntStream intStream = Stream.of(1, 2, 5, 4, 3).mapToInt((Integer n) -> {
            return n.intValue();
        });

5.14 concat

如果有两个流,希望合并成一个流,可以使用Stream接口的静态方法concat

  • 两个流合并之后,不能操作之前的流了。
  • 只支持两个流的合并,不支持直接多次合并
        Stream a = Stream.of("a");
        Stream b = Stream.of("b");
        Stream concat = Stream.concat(a, b);
        concat.forEach(System.out::println);

6. 收集Stream流中的结果

对流操作完成后,如需要将流的结果保存到数组或者集合中,可以收集流中的数据。

6.1 收集到集合

        Stream stream = Stream.of("aa", "bb", "cc");

        // 将数据收集到集合中
        List collect1 = stream.collect(Collectors.toList());
        System.out.println(collect1);  // [aa, bb, cc]

        Set collect2 = stream.collect(Collectors.toSet());
        System.out.println(collect1);  // [aa, bb, cc]

        // 收集到指定的ArrayList集合中
        ArrayList collect = stream.collect(Collectors.toCollection(ArrayList::new));

        // 收集到指定的HashSet集合中
        HashSet collect = stream.collect(Collectors.toCollection(HashSet::new));

6.1 收集到数组

        // 转成Object数组,不方便
        Stream stream = Stream.of("aa", "bb", "cc");
        Object[] objects = stream.toArray();
        for (Object object : objects) {
            System.out.println(object);
        }

        // 转成String数组
        String[] strings = stream.toArray(String[]::new);

6.3 对流中数据进行聚合计算

处理是,可以像数据库中的聚合函数一样对某个字段进行操作,如最大值maxBy,最小值minBy,求和summingInt,平均值averagingInt,统计数量counting。
仍然是stream的collect方法参数里面的Collectors的方法。

JDK8新特性Stream流_第21张图片
JDK8新特性Stream流_第22张图片
image.png

6.4 对流中的数据进行分组Collectors.groupingBy

JDK8新特性Stream流_第23张图片
image.png
JDK8新特性Stream流_第24张图片
image.png

6.5 对流中的数据进行多级分组

先根据一个字段进行分组,然后再根据另一个字段进行分组。


JDK8新特性Stream流_第25张图片
image.png

JDK8新特性Stream流_第26张图片
image.png

6.6 对流中的数据进行分区

Collectors.partitioningBy会根据值是否为true,把集合分割成两个列表,一个true列表,一个false列表。


JDK8新特性Stream流_第27张图片
image.png
JDK8新特性Stream流_第28张图片
image.png

6.7 对流中的数据进行拼接

Collectors.joining会根据指定的连接符,将所有元素连接成一个字符串。
一个参数就是拼接处是这个字符。
三个参数则是有前缀和后缀的拼接。


JDK8新特性Stream流_第29张图片
image.png
  • 到集合中:Collectors.toList()/toSet()/toCollection()
  • 到数组中:toArray()/toArray(int[]::new)
  • 聚合计算:Collectors.maxBy/minBy/counting/summingInt/averagingInt
  • 分组:Collectors.groupingBy
  • 分区:Collectors.partitioningBy
  • 拼接:Collectors.joining

7. 并行的Stream流

7.1 串行的Stream流

上面的使用的Stream流都是串行的,就是在一个线程中执行。

7. 2 并行的Stream流的两种方式

        // 1、通过parallelStream直接获取
        ArrayList list = new ArrayList();
        Stream stream1 = list.parallelStream();

        // 2、将串行流转换成并行流
        Stream parallel = Stream.of(1, 3, 4, 7).parallel();

7.3 parallelStream的线程安全问题

7.3.1 解决方法1:同步代码块

        ArrayList list = new ArrayList<>();
        Object obj = new Object();
        IntStream.rangeClosed(0,1000)
                .parallel()
                .forEach(i->{
                    synchronized (obj){
                        list.add(i);
                    }
                });
        System.out.println(list.size());

7.3.2 解决方法2:线程安全的集合

// 这两个都是线程安全的集合
Vector v = new Vector();
List integers = Collections.synchronizedList(list);


IntStream.rangeClosed(0,1000)
        .parallel()
        .forEach(i->{
             synchronized (obj){
                  v.add(i);
        }
});
        System.out.println(v.size());

7.3.3 解决方法3:调用Stream流的collect/toArray

        List collect = IntStream.rangeClosed(0, 1000)
                .parallel()
                .boxed()
                .collect(Collectors.toList());
        System.out.println(collect.size());

你可能感兴趣的