Java 8 Stream流处理

  • 1 什么是 Stream?
  • 2 Stream的API
  • 1 生成流
  • 2 forEach
  • 3 map
  • 4 flatMap
  • 5 filter
  • 6 distinct
  • 7 sorted
  • 8 limit
  • 9 reduce
  • 10 collect
  • 1 生成Collection
  • 2 生成Map
  • 3 字符串join
  • 11 统计


1 什么是 Stream?

Stream(流) 是一个来自数据源的元素队列并支持聚合操作.

  • 元素 是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
  • 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
  • 聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。

Stream操作还有两个基础的特征:

  • Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
  • 内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。

2 Stream的API

常见API总结:

操作类型

接口方法

中间操作

concat() distinct() filter() flatMap() limit() map() peek() skip() sorted() parallel() sequential() unordered()

结束操作

allMatch() anyMatch() collect() count() findAny() findFirst()

forEach() forEachOrdered() max() min() noneMatch() reduce() toArray()

区分中间操作和结束操作的方法,就是看方法的返回值,返回值为stream的是中间操作,否则是结束操作.

1 生成流

stream() − 为集合创建串行流

@Test
    public void test(){
        List<String> oldList = Arrays.asList("小红", "小蓝", "小粉", "小红", "小白", "小灰", "小青", "");
        List<String> newList = oldList.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
        System.out.println(newList);
    }
    
    //结果 过滤为空的元素
    //[小红, 小蓝, 小粉, 小红, 小白, 小灰, 小青]

parallelStream() − 为集合创建并行流

并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流

引入: Fork/Join框架

不同的数据结构,使用parallel stream的性能不同

性能

数据结构

Best

ArrayList [Concurrent]HashMaps plain arrays(阵列)

Middle


Worst

LinkedList BlockingQueues most IO-based sources

@Test
    public void test() {
        List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
        // 获取空字符串的数量
        long count = strings.parallelStream().filter(string -> string.isEmpty()).count();
        System.out.println(count);

        /* 结果
            2
        */
    }

2 forEach

Stream 提供了新的方法 forEach来迭代流中的每个数据.

@Test
    public void test(){
        Random random = new Random();
        random.ints().limit(10).forEach(System.out::println);
    }

    /* 结果
    953463696
    904482529
    1992227049
    228289483
    1260884019
    -694492700
    -1895609817
    390932419
    222813973
    1720446195
    */

3 map

map 方法用于映射每个元素到对应的结果.

@Test
    public void test(){
        List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
        // 获取对应的平方数
        List<Integer> squaresList = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());
        System.out.println(squaresList);
        //结果
        //[9, 4, 49, 25]
    }

4 flatMap

用于把原stream中的所有元素都"摊平"之后组成的Stream,转换前后元素的个数和类型都可能会改变.

@Test
    public void test() {
        Stream<List<String>> stream = Stream.of(Arrays.asList("java", "python"), Arrays.asList("go","c", "c#"));
        stream.flatMap(list -> list.stream())
                .forEach(i -> System.out.println(i));
        
        /* 结果
            java
            python
            go
            c
            c#
        */
    }

5 filter

filter 方法用于通过设置的条件过滤出元素.

@Test
    public void test(){
        List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
        // 获取空字符串的数量
        long count = strings.stream().filter(string -> string.isEmpty()).count();
        System.out.println(count);
        //结果
        //2
    }

6 distinct

作用是返回一个去除重复元素.

@Test
    public void test() {
        Stream<String> stream= Stream.of("java", "python", "c++", "java", "python");
        stream.distinct()
                .forEach(str -> System.out.println(str));

        /* 结果
            java
            python
            c++
        */
    }

7 sorted

sorted 方法用于对流进行排序.

  • 自然顺序排序 Stream<T> sorted()
  • 自定义比较器排序 Stream<T> sorted(Comparator<? super T> comparator)
@Test
    public void test() {
        Random random = new Random();
        //自然顺序排序
        random.ints().limit(10).sorted().forEach(System.out::println);

        //自定义比较器排序
        Stream<String> stream= Stream.of("I", "love", "you", "too");
        stream.sorted((str1, str2) -> str1.length()-str2.length())
                .forEach(str -> System.out.println(str));
        /* 结果
            -1333606821
            -1309783057
            -925151692
            -151014012
            326091384
            631640690
            931593578
            1222302927
            1666642944
            1787379588
            I
            you
            too
            love
        */
    }

8 limit

limit 方法用于获取指定数量的流.

@Test
    public void test() {
        Random random = new Random();
        random.ints().limit(10).forEach(System.out::println);
        
        /* 结果     
        590073840
        1940673188
        14366652
        - 1137645939
        1597476888
        1548777482
        198110723
        1269388780
        - 1953509555
        - 1577770188
        */
    }

9 reduce

reduce操作可以实现从一组元素中生成一个值,sum()max()min()count()等.

  • Optional<T> reduce(BinaryOperator<T> accumulator)
  • T reduce(T identity, BinaryOperator<T> accumulator)
  • <U> U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)
@Test
    public void test() {

        //查询字符长度最长的字符
        Stream<String> stream = Stream.of("java", "python", "c", "c++","c#");
        Optional<String> longest = stream.reduce((s1, s2) -> s1.length()>=s2.length() ? s1 : s2);
        //Optional<String> longest = stream.max((s1, s2) -> s1.length()-s2.length());
        System.out.println(longest.get());

        /* 结果
            python
        */
    }


    @Test
    public void test() {

        // 求单词长度之和
        Stream<String> stream = Stream.of("java", "python", "c", "c++","c#");

        Integer lengthSum = stream.reduce(0, //初始值
        (sum, str) -> sum+str.length(), //累加器
                (a, b) -> a+b); //部分和拼接器,并行执行

        // int lengthSum = stream.mapToInt(str -> str.length()).sum();
        System.out.println(lengthSum);

        /* 结果
            16
        */
    }

10 collect

用于将Stream转换成容器的方法.

1 生成Collection

Stream转换成List或Set

@Test
    public void test() {

        Stream<String> stream = Stream.of("java", "python", "c", "c++","c#");
        Stream<String> stream1 = Stream.of("java", "python", "c", "c++","c#");
        List<String> list = stream.collect(Collectors.toList());
        Set<String> set = stream1.collect(Collectors.toSet());

        System.out.println(list);
        System.out.println(set);
        /* 结果
            [java, python, c, c++, c#]
            [c#, python, c++, java, c]
        */
    }

指定容器的实际类型Collectors.toCollection(Supplier<C> collectionFactory)

@Test
    public void test() {

        Stream<String> stream = Stream.of("java", "python", "c", "c++","c#");
        Stream<String> stream1 = Stream.of("java", "python", "c", "c++","c#");
        ArrayList<String> arrayList = stream.collect(Collectors.toCollection(ArrayList::new));
        HashSet<String> hashSet = stream1.collect(Collectors.toCollection(HashSet::new));

        System.out.println(arrayList);
        System.out.println(hashSet);
        /* 结果
            [java, python, c, c++, c#]
            [c#, python, c++, java, c]
        */
    }

2 生成Map

  • Collectors.toMap() 指定如何生成Mapkeyvalue
  • Collectors.partitioningBy() 对元素进行二分区操作时用到
  • Collectors.groupingBy() 对元素做group操作时用到

Collectors.toMap

@Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
       private String id;
       private String name;
       private Integer age;
       private String sex;
    }
    @Test
    public void test() {
		//1 指定key-value,value是对象中的某个属性值
		 Map<Integer,String> userMap1 = userList.stream().collect(Collectors.toMap(User::getId,User::getName));
        
        //2 指定key-value,value是对象本身
        Map<Integer,User> userMap2 = userList.stream().collect(Collectors.toMap(User::getId,User->User));
        
        //3 指定key-value,value是对象本身,Function.identity()是简洁写法,返回对象本身
        Map<Integer,User> userMap3 = userList.stream().collect(Collectors.toMap(User::getId, Function.identity()));
        
        //4 指定key-value,value是对象本身,Function.identity()是简洁写法,返回对象本身,key 冲突的解决办法,这里选择新的key覆盖旧的key
         Map<Integer,User> userMap4 = userList.stream().collect(Collectors.toMap(User::getId, Function.identity(),(key1,key2)->key2));
        
    }

Collectors.partitioningBy()

用于将Stream中的元素依据某个二值逻辑(满足条件,或不满足)分成互补相交的两部分,如男女性别,及格与否

@Test
    public void test() {
		Map<Boolean, List<User>> userMap5 = userList.stream().collect(Collectors.partitioningBy(s -> s.getAge() >= 40));
    }

Collectors.groupingBy()

按照某个属性对数据进行分组,属性相同的元素会被对应到Map的同一个key上.

@Test
    public void test() {
        //根据男女分组,分别统计两组的人数
		Map<String, Integer> totalByDept = userList.stream()
                    .collect(Collectors.groupingBy(User::getSex(),
                                                   Collectors.counting()));// 下游收集器
    }

	//下游收集器中还可以包含更下游的收集器
	@Test
    public void test() {
        //根据男女分组,并统计两组的所有人员名称
		Map<String,List<String>> totalByDept = userList.stream()
                    .collect(Collectors.groupingBy(User::getSex(),
                                Collectors.mapping(User::getName,// 下游收集器
                                Collectors.toList())));// 更下游的收集器
    }

3 字符串join

字符串拼接时使用Collectors.joining()生成的收集器

@Test
    public void test() {

        // 使用Collectors.joining()拼接字符串
        Stream<String> stream = Stream.of("I", "love", "you");
        // String joined1 = stream.collect(Collectors.joining());
        // System.out.println(joined1);
        // String joined2 = stream.collect(Collectors.joining(","));
        // System.out.println(joined2);
        String joined3 = stream.collect(Collectors.joining(",", "{", "}"));
        System.out.println(joined3);

            /*结果
            Iloveyou
            I,love,you
            {I,love,you}
            */

    }

11 统计

一些产生统计结果的收集器也非常有用。主要用于int、double、long等基本类型上.

@Test
    public void test() {
        List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);

        IntSummaryStatistics stats = numbers.stream().mapToInt((x) -> x).summaryStatistics();

        System.out.println("列表中最大的数 : " + stats.getMax());
        System.out.println("列表中最小的数 : " + stats.getMin());
        System.out.println("所有数之和 : " + stats.getSum());
        System.out.println("平均数 : " + stats.getAverage());

        /* 结果
            列表中最大的数 : 7
            列表中最小的数 : 2
            所有数之和 : 25
            平均数 : 3.5714285714285716
        */
    }