JAVA之深度掌握Java Stream 流操作

参考地址:

https://juejin.cn/post/6980135669368619045

https://juejin.cn/post/6844903830254010381

一、Stream简介

Stream 将要处理的元素集合看作一种流,在流的过程中,借助Stream API对流中的元素进行操作,比如:筛选、排序、聚合等

JAVA、基础技术、技术与框架JAVA之深度掌握Java Stream 流操作插图

Stream的操作符大体上分为两种:中间操作符和终止操作符号

中间操作符

  • map(mapToInt,mapToLong,mapToDouble) 转换操作符,把比如A->B,这里默认提供了转int,long,double的操作符。
  • flatmap(flatmapToInt,flatmapToLong,flatmapToDouble) 拍平操作比如把 int[]{2,3,4} 拍平 变成 2,3,4 也就是从原来的一个数据变成了3个数据,这里默认提供了拍平成int,long,double的操作符。
  • limit 限流操作,比如数据流中有10个 我只要出前3个就可以使用。 distint 去重操作,对重复元素去重,底层使用了equals方法。 filter 过滤操作,把不想要的数据过滤。
  • peek 挑出操作,如果想对数据进行某些操作,如:读取、编辑修改等。
  • skip 跳过操作,跳过某些元素。
  • sorted(unordered) 排序操作,对元素排序,前提是实现Comparable接口,当然也可以自定义比较器。

终止操作符

  • collect 收集操作,将所有数据收集起来,这个操作非常重要,官方的提供的Collectors 提供了非常多收集器,可以说Stream 的核心在于Collectors。
  • count 统计操作,统计最终的数据个数。
  • findFirst、findAny 查找操作,查找第一个、查找任何一个 返回的类型为Optional。
  • noneMatch、allMatch、anyMatch 匹配操作,数据流中是否存在符合条件的元素 返回值为bool 值。
  • min、max 最值操作,需要自定义比较器,返回数据流中最大最小的值。
  • reduce 规约操作,将整个数据流的值规约为一个值,count、min、max底层就是使用reduce。
  • forEach、forEachOrdered 遍历操作,这里就是对最终的数据进行消费了。
  • toArray 数组操作,将数据流的元素转换成数组。

二、Stream例子:

2.1Stream的创建

2.1.1List<String>的创建方式

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

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);

2.1.2List<class>的创建方式

2.1.2.1首先创建一个MyUser类
package com.fcors.fcors;

import lombok.*;

@Builder
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class MyUser {
    private String name;
    private String sex;
    private int age;

}
2.1.2.2创建List<class>
package com.fcors.fcors;

import org.junit.Test;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class StreamTest {
    public List<MyUser> DefaultMyUser(){
        MyUser user1 = MyUser.builder()
                .name("fox1")
                .sex("boy")
                .age(18)
                .build();
        MyUser user2 = MyUser.builder()
                .name("fox2")
                .sex("boy")
                .age(20)
                .build();

        MyUser user3 = MyUser.builder()
                .name("fox3")
                .sex("girl")
                .age(16)
                .build();
        List<MyUser> UserList = new ArrayList<>();
        UserList.add(user1);
        UserList.add(user2);
        UserList.add(user3);
        return UserList;
    }

    @Test
    public void testStream() {
        List<MyUser> UserList = this.DefaultMyUser();
        System.out.println(UserList);
        // 创建一个顺序流
        List<MyUser> streamList = UserList.stream().collect(Collectors.toList());;
        System.out.println(streamList);
        // 创建一个并行流
        List<MyUser> streamList2 = UserList.parallelStream().collect(Collectors.toList());;
        System.out.println(streamList);


    }
}
JAVA、基础技术、技术与框架JAVA之深度掌握Java Stream 流操作插图1

2.2中间操作符

2.2.1映射(map/flatMap)

map:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。

2.2.1.1List<String>型的Map
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);
    }
}
2.2.1.2 List<class>的Map,遍历每个一个Class处理
    @Test
    public void testStream() {
        List<MyUser> UserList = this.DefaultMyUser();
        System.out.println(UserList);
        // 创建一个顺序流
        List<MyUser> streamList = UserList.stream()
                .map(u ->{
                    u.setName(u.getName().toUpperCase());
                    return u;
                })
                .collect(Collectors.toList());;
        System.out.println(streamList);

    }
JAVA、基础技术、技术与框架JAVA之深度掌握Java Stream 流操作插图2
2.2.1.3利用Map提取class中的name并拼接成字符串
 @Test
    public void testStream() {
        List<MyUser> UserList = this.DefaultMyUser();
        System.out.println(UserList);
        // 创建一个顺序流
        String phrase = UserList
                .stream()
                .map(p -> p.getName()) // 提取名字
                .collect(Collectors.joining(" and ", "In Germany ", " are of legal age.")); // 以 In Germany 开头,and 连接各元素,再以 are of legal age. 结束

        System.out.println(phrase);
    }
    /**输出:In Germany fox1 and fox2 and fox3 are of legal age.**/

2.2.2flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。

String poetry = "Where, before me, are the ages that have gone?\n" +
        "And where, behind me, are the coming generations?\n" +
        "I think of heaven and earth, without limit, without end,\n" +
        "And I am all alone and my tears fall down.";
Stream<String> lines = Arrays.stream(poetry.split("\n"));
Stream<String> words = lines.flatMap(line -> Arrays.stream(line.split(" ")));
List<String> l = words.map( w -> {
    if (w.endsWith(",") || w.endsWith(".") || w.endsWith("?"))
        return w.substring(0,w.length() -1).trim().toLowerCase();
    else
        return w.trim().toLowerCase();
}).distinct().sorted().collect(Collectors.toList());
System.out.println(l); 
//[ages, all, alone, am, and, are, before, behind, coming, down, earth, end, fall, generations, gone, have, heaven, i, limit, me, my, of, tears, that, the, think, where, without]

2.2.3 筛选(filter)

    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);
    }
    public void testStream() {
        List<MyUser> UserList = this.DefaultMyUser();
        System.out.println(UserList);
        List<MyUser> streamList = UserList.stream()
                .filter(p -> p.getAge()>18 && p.getSex()=="girl")
                .collect(Collectors.toList());;
        System.out.println(streamList);
    }

2.2.4limit 限流操作

    @Test
    public void StreamMapObj(){
        List<MyUser> UserList = this.DefaultMyUser();
        System.out.println("初始化Stream:"+UserList);
        // 创建一个顺序流
        List<MyUser> streamList = UserList.stream()
                .filter(u ->{
                    return u.getName().contains("fox");
                }).limit(2)
                .collect(Collectors.toList());
        System.out.println(streamList);

    }

2.2.5 distinct 去重

整体去重

public void StreamMapObj(){
    List<MyUser> UserList = this.DefaultMyUser();
    System.out.println("初始化Stream:"+UserList);
    // 创建一个顺序流
    List<MyUser> streamList = UserList.stream()
            .filter(u ->{
                return u.getName().contains("fox");
            }).distinct()
            .collect(Collectors.toList());
    System.out.println(streamList);
}

根据某个属性去重

    static <T> Predicate<T> distinctByKey1(Function<? super T, ?> keyExtractor) {
        val seen = new ConcurrentHashMap<>();
        return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }
    @Test
    public void StreamMapObj(){
        List<MyUser> UserList = this.DefaultMyUser();
        System.out.println("初始化Stream:"+UserList);

        UserList.stream().filter(distinctByKey1(s -> s.getSex()))
                .forEach(System.out::println);

Concat流合并

    @Test
    public void StreamConcat(){
        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());

        System.out.println(newList);
    }

skip 跳过操作

一维

    @Test
    public void StreamPage(){
        int page = 2;
        int pagesize = 3;
        int pageSkip = page==1 ?0 :1*pagesize;
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        List<Integer> dataList = numbers.stream().sorted(Integer::compareTo).skip(pageSkip).limit(pagesize).collect(Collectors.toList());
        System.out.println(dataList.toString());
    }

数组型list

    @Test
    public void StreamPageList(){
        int page = 2;
        int pagesize = 2;
        int pageSkip = page==1 ?0 :1*pagesize;
        List<MyUser> UserList = this.DefaultMyUser();
        List<MyUser> streamList = UserList.stream().skip(pageSkip).limit(pagesize).collect(Collectors.toList());
        System.out.println(streamList);

    }

排序

一维排序参看上面“skip 跳过操作 ”

数组型Stream

自然排序

    @Test
    public void StreamPageList(){
        int page = 1;
        int pagesize = 2;
        int pageSkip = page==1 ?0 :1*pagesize;
        List<MyUser> UserList = this.DefaultMyUser();
        List<MyUser> streamList = UserList.stream().sorted(Comparator.comparing(MyUser::getName)).skip(pageSkip).limit(pagesize).collect(Collectors.toList());
        System.out.println(streamList);

    }

倒序排序

    @Test
    public void StreamPageList(){
        int page = 1;
        int pagesize = 2;
        int pageSkip = page==1 ?0 :1*pagesize;
        List<MyUser> UserList = this.DefaultMyUser();
        List<MyUser> streamList = UserList.stream().sorted(Comparator.comparing(MyUser::getSex).reversed().thenComparing(MyUser::getName)).skip(pageSkip).limit(pagesize).collect(Collectors.toList());
        System.out.println(streamList);

    }

终止操作符

collect收集操作

    @Test
    public void StreamCollect(){
        List<MyUser> UserList = this.DefaultMyUser();
        List<MyUser> streamList = UserList.stream().collect(Collectors.toList());
        System.out.println("List"+streamList);
        Set<MyUser> streamSet = UserList.stream().collect(Collectors.toSet());
        System.out.println("Set"+streamList);

        Map<String, MyUser> map = UserList.stream()
                .collect(Collectors.toMap(MyUser::getName, p -> p));
        System.out.println("Map"+map);
    }

统计操作(max/average/sum)

    @Test
    public void StreamMatch(){
        List<MyUser> UserList = this.DefaultMyUser();
        Double AvgeAge = UserList.stream().collect(Collectors.averagingInt(MyUser::getAge));
        System.out.println("员工平均年龄:" + AvgeAge);
        Optional<Integer> max = UserList.stream().map(MyUser::getAge).max(Integer::compare);
        System.out.println("员工最大年龄:" + max.get());

        int sum = UserList.stream().mapToInt(MyUser::getAge).sum();
        System.out.println("员工年龄汇总:" + sum);

    }

分组

  • partitioningBy按是否满足条件分组
  • groupingBy分组
    @Test
    public void StreamMatch(){
        List<MyUser> UserList = this.DefaultMyUser();
        // 将员工按年龄20岁分区
        Map<Boolean, List<MyUser>> part = UserList.stream().collect(Collectors.partitioningBy(x -> {
            return "boy".equals(x.getSex());
        }));
        System.out.println("part"+part);
        //part{false=[MyUser(name=fox3, sex=girl, age=16)], true=[MyUser(name=fox1, sex=boy, age=18), MyUser(name=fox2, sex=boy, age=20), MyUser(name=fox19, sex=boy, age=18)]}
        Map<String, List<MyUser>> group1 = UserList.stream().collect(Collectors.groupingBy(MyUser::getSex));
        System.out.println("group"+group1);
        //group{girl=[MyUser(name=fox3, sex=girl, age=16)], boy=[MyUser(name=fox1, sex=boy, age=18), MyUser(name=fox2, sex=boy, age=20), MyUser(name=fox19, sex=boy, age=18)]}
        Map<String, Map<Integer, List<MyUser>>> group2 = UserList.stream().collect(Collectors.groupingBy(MyUser::getSex, Collectors.groupingBy(MyUser::getAge)));
        System.out.println("group2"+group2);
        //group2{girl={16=[MyUser(name=fox3, sex=girl, age=16)]}, boy={18=[MyUser(name=fox1, sex=boy, age=18), MyUser(name=fox19, sex=boy, age=18)], 20=[MyUser(name=fox2, sex=boy, age=20)]}}
    }

遍历foreach/findFirst/findAny/anyMatch

        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);

Stream的复用性

    @Test
    public void StreamMu(){
        Stream<String> stream =
                Stream.of("d2", "a2", "b1", "b3", "c")
                        .filter(s -> s.startsWith("a"));

        stream.anyMatch(s -> true);    // ok
        stream.noneMatch(s -> true);   // exception
    }

改造办法

    @Test
    public void StreamMu(){
        Supplier<Stream<String>> streamSupplier =
                () -> Stream.of("d2", "a2", "b1", "b3", "c")
                        .filter(s -> s.startsWith("a"));

        boolean bool1 =  streamSupplier.get().anyMatch(s -> true);   // ok callback true
        System.out.println(bool1);
        boolean bool2 = streamSupplier.get().noneMatch(s -> true);  // ok callback false
        System.out.println(bool2);
    }

stream生成VO

    @Test
    public void StreamVo() {
        List<MyUser> UserList = this.DefaultMyUser();
        List<MyUserVO> UserVoList = (List<MyUserVO>) UserList.stream().map(User->{
                    MyUserVO myUserVO = new MyUserVO();
            try {
                BeanUtils.copyProperties(myUserVO,User );
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
            return myUserVO;

                }).collect(Collectors.toList());
        System.out.println(UserVoList);
    }

reduce

   private BigDecimal getSumByCategory(List<SkuOrderBO> skuOrderBOList, Long cid) {
        BigDecimal sum = skuOrderBOList.stream()
                .filter(sku -> sku.getCategoryId().equals(cid))
                .map(SkuOrderBO::getTotalPrice)
                .reduce(BigDecimal::add)
                .orElse(new BigDecimal("0"));
        return sum;