2021/11/12 西安 Java8 Stream流

Java8 Stream

Java8,Stream API 提供了一种高效且易于使用的处理数据的方式

Stream自己不会存数据,而且它也不能改变源对象,而是返回一个新的Stream。

惰性求值,延迟加载:

终止操作:一次性处理全部Stream是有延迟操作的,意思是执行了终止操作(Terminal),才会去执行中间链

Stream使用过程:

中间操作的返回结果都是Stream,故可以多个中间操作叠加;终止操作用于返回我们最终需要的数据,只能有一个终止操作。

每次操作都会返回一个新的流对象,方便进行链式操作,但原有的流对象会保持不变。

1、创建Stream
从一个数据源(数组,集合)获取流。
2、中间操作
一个中间操作链,对数据源的数据进行处理。
3、终止操作
一旦终止了,这个Stream就到此为止了,不可以再使用了


创建Stream流

1、集合和数组

        //1. 通过集合提供 stream() 方法
        List<String> list = new ArrayList<>();
        Stream<String> stream1 = list.stream();

        //2. 通过 Arrays 提供 stream() 方法
        //stream()是Arrays类的泛型静态方法,泛型类型由传入的数组类型确定   public static <T> Stream<T> stream(T[] array)
        Integer[] nums = new Integer[10];
        Stream<Integer> stream2 = Arrays.stream(nums);

2、Stream.of()

泛型静态方法,泛型类型由传入的类型确定

public static<T> Stream<T> of(T... values)

Stream.of(T... values)⽅法是Stream接⼝的⼀个静态⽅法,其底层调⽤的是 Arrays.stream(T[] array)⽅法

        //3. 通过 Stream 提供 of() 方法
        Stream<Integer> stream3 = Stream.of(1, 2, 3, 4, 5);

        //这里T是什么呢   public static<T> Stream<T> of(T... values)
        Stream<? extends Serializable> stream = Stream.of(1, 2, "1", 4, 5);

3、迭代和生成

    public void test1(){
        //4. 创建无限流2种方式,迭代和生成
        //①迭代
        Stream<Integer> stream4 = Stream.iterate(0, (s) -> s + 2);
        stream4.limit(3).forEach(System.out::println);

        //②生成
        Stream<Double> stream5 = Stream.generate(Math::random);
        stream5.limit(3).forEach(System.out::println);
    }


Stream流中间操作

准备的数据

实体类:用lombok省略了set/get...

@NoArgsConstructor
@AllArgsConstructor
@Data
public class Employee {
    private String name;
    private Integer age;
    private Double salary;
}

测试数据准备:

List<Employee> employees = Arrays.asList(
        new Employee("张三", 18, 9999.99),
        new Employee("李四", 20, 3333.33),
        new Employee("王五", 35, 7777.77),
        new Employee("赵六", 8, 6666.66),
        new Employee("田七", 19, 5555.55)
);

1、过滤、去重

filter——接收 Lambda , 从流中排除某些元素。
distinct——去重,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
List<Employee> employees = Arrays.asList(
        new Employee("张三", 18, 9999.99),
        new Employee("李四", 20, 3333.33),
        new Employee("王五", 35, 7777.77),
        new Employee("赵六", 8, 6666.66),
        new Employee("田七", 19, 5555.55)
);
Stream<Employee> stream=employees.stream();
//Stream<T> filter(Predicate<? super T> predicate);
stream.filter(e->e.getSalary()>7000).forEach(System.out::println);

多条件过滤:

Predicate接口时候可能需要进行判断条件的拼接

  1. 而and方法相当于是使用&&来拼接两个判断条件;
  2. 而or方法相当于是使用||来拼接两个判断条件。
  3. negate方法相当于是在判断添加前面加了个! 表示取反
    public static void main(String[] args) throws IOException {
        List<Employee> employees = Arrays.asList(
                new Employee("张三", 18, 9999.99),
                new Employee("李四", 20, 3333.33),
                new Employee("王五", 35, 7777.77),
                new Employee("赵六", 28, 6666.66),
                new Employee("田七", 19, 5555.55)
        );

        //获取工资大于5000且年龄大于18的员工
        employees.stream().filter(new Predicate<Employee>() {
            @Override
            public boolean test(Employee employee) {
                return employee.getSalary() > 5000;
            }
        }.and(new Predicate<Employee>() {
            @Override
            public boolean test(Employee employee) {
                return employee.getAge() > 18;
            }
        })).forEach(System.out::println);
    }


2、前向截断、后向截断

limit——截断流,使其元素不超过给定数量。 skip(n) —— 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。

如根据分页参数截取:对list集合分页截取分页,返回当前页

//1.计算出需要跳过多少条数据(流切片的起始位置)
int startPosition = (pageNo-1)*pageSize;
List<T> allCount = service.getAllCount();
Stream<T> stream = allCount.stream().skip(startPosition).limit(pageSize);
List<T> resultAccountList = stream.collect(Collectors.toList());

//2.可以写成一行
List<AssetsDirectoriesVo> res = list.stream() .skip((pageBaseReq.getPageNum() - 1) *pageBaseReq.getPageSize()).limit(pageBaseReq.getPageSize()).collect(Collectors.toList()); //开始分页


3、排序 sorted()

sorted()——自然排序
sorted(Comparator com)——定制排序
    @Test
    public void test1(){
        //使员工按照工资排序
        employees.stream()
                .sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))
                .forEach(System.out::println);
    }


4、map函数

集合里每一个元素都会走一遍这个函数并将其映射成一个新的元素。

    public void test1(){
        List<String> strList = Arrays.asList("www", "atguigu", "com");
        //将每一个元素与映射为大写  也可以写为 map((s) -> s.toUpperCase())
        strList.stream().map(String::toUpperCase).forEach(System.out::println);
        System.out.println("----------------------------");
        //收集员工的姓名
        employees.stream().map(Employee::getName).forEach(System.out::println);
    }

5、flatmap函数

对流扁平化处理:flatmap方法让你把一个流中的每个值都换成另一个流,然后把所有的流连接起来成为一个流。

与map函数不同的是,flatmap函数可以对stream流中单个元素再进行拆分(切片),从另一种角度上说,使用了它,就是使用了双重for循环。

flatMap()的作用是对流中的元素进行1对多的转换,然后将结果元素展开为新的流。

这句话的含义是什么?你可以理解为flatMap就是两层for循环处理数据。而返回结果就是flatMap展开的流。


Stream流终止操作

1、匹配,allMatch,anyMatch,noneMatch

allMatch——检查是否匹配所有元素
anyMatch——检查是否至少匹配一个元素
noneMatch——检查是否没有匹配所有元素

    @Test
    public void test1(){
//        allMatch——检查是否匹配所有元素
        boolean b1 = employees.stream().allMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));
        System.out.println(b1);

//        anyMatch——检查是否至少匹配一个元素
        boolean b2 = employees.stream().anyMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));
        System.out.println(b2);

//        noneMatch——检查是否没有匹配所有元素
        boolean b3 = employees.stream().noneMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));
        System.out.println(b3);
    }


2、查找,findFirst,findAny,count,max, min

findFirst——返回第一个元素
findAny——返回当前流中的任意元素
count——返回流中元素的总个数
max——返回流中最大值
min——返回流中最小值

    @Test
    public void test1(){
//        findFirst——返回第一个元素
        Optional<Employee> op = employees.stream()
                .sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))
                .findFirst();

        System.out.println(op.get());

        System.out.println("------------------------------------");
//        findAny——返回当前流中的任意元素
        Optional<Employee> op2 = employees.parallelStream()
                .sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))
                .findAny();
        System.out.println(op2.get());

        System.out.println("-----------------------------------");

//        count——返回流中元素的总个数
        long count = employees.stream().count();
        System.out.println(count);

        System.out.println("-----------------------------------");

//        max——返回流中最大值
        Optional<Employee> max = employees.stream().max((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
        System.out.println(max.get());

        System.out.println("-----------------------------------");

//        min——返回流中最小值
        Optional<Employee> min = employees.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
        System.out.println(min.get());
    }


3、归约 reduce

reduce(T identity, BinaryOperator) / reduce(BinaryOperator) ——可以将流中元素反复结合起来,得到一个值。【将流规约成一个值】

你可以将这个操作看成把一张长长的纸(你的流)反复折叠成一个小方块,而这就是规约操作的结果。

reduce ⽅法这⾥做的是:

从前两个元素开始,进⾏某种操作(我这⾥进⾏的是加法操作)后,返回⼀个结果,然后再拿这个结果跟第三个元素执⾏同样的操作,以此类推,直到最后的⼀个 元素。
 public static void main(String[] args) {
     Integer[] array = {1,2,3,4,5,6,7,8,9,10};
     //使用规约对数组求和
     Stream<Integer> stream = Arrays.stream(array);
     Optional<Integer> optional = stream.reduce((a, b) -> {
         System.out.println(String.format("%s: %d + %d = %d",
                 Thread.currentThread().getName(), a, b, a + b));
         return a + b;
     });
     System.out.printf("数组array元素和:%d",optional.get());
 }

默认情况下,它是在⼀个单线程运⾏的,也就是main线程。然后每次reduce操作都是串⾏起来的,⾸先计算前两个数字的和,然后再往后依次计算

-----------------------------------------------------------
parallel()可以改为并行计算【底层原理是使⽤了 Fork/Join 框架】

改为并行后控制台打印输出,虽然计算过程看着乱乱的。。但是计算结果完全正确


4、迭代forEach

外部迭代是使用迭代器遍历

List<Employee> employees = Arrays.asList(
        new Employee("张三", 18, 9999.99),
        new Employee("李四", 20, 3333.33),
        new Employee("王五", 35, 7777.77),
        new Employee("赵六", 28, 6666.66),
        new Employee("田七", 19, 5555.55)
);
//stream().forEach() 内部迭代
employees.stream().forEach(System.out::println);
//使用集合的遍历操作 外部迭代
employees.forEach(System.out::println);

收集操作:

1、collect(Collector c)

Collector 接口中方法的实现决定了如何对流执行收集操作(如收集到 List、Set、Map)。但是 Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例

    @Test
    public void test1(){
        //用list集合收集 Collectors.toList()
        List<String> list = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.toList());
        System.out.println(list);
        //用Set集合收集 Collectors.toSet()
        Set<String> set = employees.stream().map(Employee::getName).collect(Collectors.toSet());
        //用HashSet集合收集 Collectors.toCollection(HashSet::new)
        HashSet<String> hs = employees.stream().map(Employee::getName).collect(Collectors.toCollection(HashSet::new));
    }

2、使用Stream流实现5大聚合函数

    @Test
    public void test6(){
        DoubleSummaryStatistics ds = employees.stream().collect(Collectors.summarizingDouble(Employee::getSalary));

        //总工资
        System.out.println(ds.getSum());

        //平均值
        System.out.println(ds.getAverage());

        //最大值
        System.out.println(ds.getMax());

        //最小值
        System.out.println(ds.getMin());

        //个数
        System.out.println(ds.getCount());
    }

3、分组 根据某属性值对流分组

List<Employee> employees = Arrays.asList(
        new Employee("张三", 18, 9999.99),
        new Employee("李四", 19, 3333.33),
        new Employee("王五", 35, 7777.77),
        new Employee("赵六", 18, 6666.66),
        new Employee("田七", 19, 5555.55)
);
Map<Integer, List<Employee>> map = employees.stream().collect(Collectors.groupingBy(Employee::getAge));
map.forEach((age,employeeList)->{
    System.out.println(age+"==>"+employeeList);
    System.out.println("==================");
});

 控制台打印

这个对集合分组,在后期的项目中就遇到了,因为是从mongodb中查询出来的医院科室数据,我们要显示一个科室树,按照大科室分组。大科室下有很多小科室

    //2.根据大科室编号  bigcode 分组,获取每个大科室里面下级子科室
    Map<String, List<Department>> deparmentMap =
 	departmentList.stream().collect(
        Collectors.groupingBy(Department::getBigcode)
    );

没错,Department就是科室,下图就是mongodb存储的科室信息


连接流中每个字符串

    @Test
    public void test8(){
        String str = employees.stream().map(Employee::getName).collect(Collectors.joining(",", "【", "】"));
        System.out.println(str);
    }

 


jdk1.7  Fork/Join框架

多线程优势多线程尽可能去利用cpu的资源

将一个大任务,进行拆分(fork)成若干个小任务(拆到不可再拆时),再将一个个的小任务运算的结果进行 join 汇总。

 -------工作窃取模式【随机在别的线程偷一个任务执行】

并行流  并行流底层原理是 Fork/Join框架 ,尽可能去利用cpu的资源

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

Stream API 可以声明性地通过 parallel() 与 sequential() 在并行流与顺序流之间进行切换。


4、list转map

Map<String, SysRoleDto> roleMap = null;
List<SysRoleDto> roleDtoList = null;
if (ids.size() > 0) {
    roleDtoList = projectUserRoleDaoMapper.selectUserRoleInfoByUserIds(ids);
    //key冲突时key的选择(这里是选择第二个key覆盖第一个key)
    roleMap = roleDtoList.stream().collect(Collectors.toMap(SysRoleDto::getUseUserId, Function.identity(), (key1, key2) -> key2));
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值