JDK8新特性-stream常用API

链接: stream.
链接: 浅谈 jdk 中的 Stream 流使用及原理.
链接: stream流排序.
链接: Java 8 Steam API map和flatMap方法使用详解.

前提了解

lambda表达式、链式编程、函数式接口

函数式接口:只有一个方法的接口

  1. Function函数式接口 传入参数T 返回参数R
  2. 断定型接口:有一个输入参数,返回值只能是 布尔值
  3. Consumer 消费型接口 只有输入 没有返回值
  4. Supplier 供给型接口 没用输入,只有返回值

为什么要使用stream

  • Java 8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。Stream API 借助于同样新出现的 Lambda 表达式,极大的提高编程效率和程序可读性。同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用 fork/join 并行方式来拆分任务和加速处理过程。通常编写并行代码很难而且容易出错, 但使用 Stream API 无需编写一行多线程的代码,就可以很方便地写出高性能的并发程序。
  • 能大大减轻数据库的压力,数据库专注于存储,计算交由程序处理

在这里插入图片描述

简单解释一下表格中加粗字体的含义:

  • 中间操作:从字面上看是流从开始到结束中间的一环,从操作结果上看是上一个流经过中间操作生成了下一个流,从代码上看实际上是生成了一个个 stage 和 sink节点,至于 sink 节点是什么,后面的篇幅会分析
  • 结束操作:和中间操作相反,结束操作之后是流的最后一个环节,不再生成新的流,从代码上看是启动整个 sink 节点从头开始运行
  • 有状态:简单理解就是需要获取流中所有数据之后才能进行的操作,比如 sort 排序,肯定要获取了所有的元素之后才能排序
  • 无状态:不需要获取所有元素,只需要对单个元素进行的操作,比如 filter 过滤,只针对每一个元素本身所做的操作
  • 短路:终结操作中只要有一个元素可以得到操作结果的数据,比如 findAnyMatch,只要有一个节点满足筛选条件就会返回 true
  • 非短路:需要遍历所有的节点才能得到预期结果,比如 forEach、max

使用时注意处理空指针异常

数据准备

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

public class Test {

    public static List<User> init() {
        List<User> userList = new ArrayList<User>();
        userList.add(new User(1, "小a", "男", 32, BigDecimal.valueOf(1600)));
        userList.add(new User(2, "小b", "男", 30, BigDecimal.valueOf(1800)));
        userList.add(new User(3, "小c", "女", 20, BigDecimal.valueOf(1700)));
        userList.add(new User(4, "小d", "男", 38, BigDecimal.valueOf(1500)));
        userList.add(new User(5, "小e", "女", 25, BigDecimal.valueOf(1200)));
        return userList;
    }


    public static void main(String[] args) {
        List<User> list = Test.init();

    }

}

常用API

Java8 根据对象某个属性值合并list


List<V> list = new ArrayList<>(tdwjcwphcqktj1.stream()
                .collect(Collectors.toMap(V::getCode, a -> a, this::mergeObject)).values());

    /**
     * 合并对象
     *
     * @param o1
     * @param o2
     * @return o1
     */
    private T mergeObject(T o1, T o2) {
        o1.setYhczs(o1.getYhczs().add(o2.getYhczs()));
        return o1;
    }
    

Java8 根据对象某个属性值去重

        List<LzgdJdjcPlthDTO> uniqueSkuValues = all.stream().collect(
                Collectors.collectingAndThen(Collectors.toCollection(
                        () -> new TreeSet<>(Comparator.comparing(o -> o.getJcbh()))), ArrayList::new)
        );

list过滤

list.removeIf(o -> "小a".equals(o.getName()));

forEach()

list.forEach(System.out::println);

filter(T -> boolean) 过滤列表数据

 list.stream().filter(o -> o.getUserId()!=null && o.getUserId() == 1)
 .collect(Collectors.toList());

findAny() 和 findFirst() 获取第一条数据

搭配 filter 操作可实现 selectOne()
注意: 建议使用主键id查找,findFirst() 和 findAny() 都是获取列表中的第一条数据,但是findAny()操作,返回的元素是不确定的,对于同一个列表多次调用findAny()有可能会返回不同的值。使用findAny()是为了更高效的性能。如果是数据较少,串行地情况下,一般会返回第一个结果,如果是并行(parallelStream并行流)的情况,那就不能确保是第一个。

User user = list.stream()
        .findFirst().orElse(null);
System.out.println(user);
User user1 = list.stream()
        .filter(o -> o.getUserId() != null && o.getUserId() == 5)
        .findAny().orElse(null);
System.out.println(user1);

map(T -> R)

使用 map() 将流中的每一个元素 T 映射为 R(类似类型转换)

List<Integer> collect = list.stream()
    .map(User::getUserId).collect(Collectors.toList());
System.out.println(collect);

flatMap(T -> Stream)

使用 flatMap() 将流中的每一个元素 T 映射为一个流,再把每一个流连接成为一个流

多重集合 嵌套时有妙用

 //创建城市
        List<List<String>> cityList = new ArrayList<>();
        List<String> cityList1 = new ArrayList<String>();
        cityList1.add("福州");
        cityList1.add("宁德");
        List<String> cityList2 = new ArrayList<String>();
        cityList2.add("深圳");
        cityList2.add("广州");
        cityList.add(cityList1);
        cityList.add(cityList2);

        List<String> collect = cityList.stream()
                .flatMap(it -> it.stream())
                .collect(Collectors.toList());
        System.out.println(collect);

distinct()

使用 distinct() 方法可以去除重复的数据

 List<String> cityList = new ArrayList<String>();
        cityList.add("福州");
        cityList.add("宁德");
        cityList.add("宁德");
        List<String> collect = cityList.stream().distinct().collect(Collectors.toList());
        System.out.println(collect);

// 根据对象的某个属性值  或 多个 
        List<ZbglXzpgDataTableVo> cbList = tableList
                .stream()
                .collect(
                        Collectors.collectingAndThen(
                                Collectors.toCollection(
                                        () -> new TreeSet<>(
                                                Comparator.comparing(
                                                        tc -> tc.getKey()))), ArrayList::new));


limit(long n) 和 skip(long n)

limit(long n) 方法用于返回前n条数据,skip(long n) 方法用于跳过前n条数据

List<User> collect = list.stream().limit(1).collect(Collectors.toList());
System.out.println(collect);

anyMatch(T -> boolean)

使用 anyMatch(T -> boolean) 判断流中是否有一个元素匹配给定的 T -> boolean 条件。

 boolean b = list.stream().anyMatch(o -> o.getUserId().equals(1));
 System.out.println(b);

allMatch(T -> boolean)

使用 allMatch(T -> boolean) 判断流中是否所有元素都匹配给定的 T -> boolean 条件。

boolean b = list.stream().allMatch(o -> o.getUserId().equals(1));
System.out.println(b);

noneMatch(T -> boolean)

使用 noneMatch(T -> boolean) 流中是否没有元素匹配给定的 T -> boolean 条件。

boolean b = list.stream()
                .filter(o -> o!=null && o.getUserId()!=null)
                .noneMatch(o -> o.getUserId().equals(10));
System.out.println(b);

reduce 用于组合流中的元素,如求和,求积,求最大值等

mapToInt(T -> int) 、
mapToDouble(T -> double) 、
mapToLong(T -> long)

//用户列表中年龄的最大值、最小值、总和 注意空值处理
        int maxVal = list.stream().map(User::getAge).filter(Objects::nonNull).reduce(Integer::max).get();
        int minVal = list.stream().map(User::getAge).filter(Objects::nonNull).reduce(Integer::min).get();
        int sumVal = list.stream().map(User::getAge).filter(Objects::nonNull).reduce(0,Integer::sum);
        System.out.println(maxVal);
        System.out.println(minVal);
        System.out.println(sumVal);

使用 count() 可以对列表数据进行统计

        long count = list.stream().filter(o -> {
                    String gender = Optional.ofNullable(o)
                            .map(User::getGender)
                            .orElse("");
                    return "男".equals(gender);
                }
        ).count();
        System.out.println(count);

averagingInt()、averagingLong()、averagingDouble()

double aveAge = list.stream().collect(Collectors.averagingDouble(User::getAge));
System.out.println(aveAge);

summarizingInt()、summarizingLong()、summarizingDouble()

IntSummaryStatistics类提供了用于计算的平均值、总数、最大值、最小值、总和等方法

        //获取IntSummaryStatistics对象
        IntSummaryStatistics ageStatistics = list.stream().collect(Collectors.summarizingInt(User::getAge));
        //统计:最大值、最小值、总和、平均值、总数
        System.out.println("最大年龄:" + ageStatistics.getMax());
        System.out.println("最小年龄:" + ageStatistics.getMin());
        System.out.println("年龄总和:" + ageStatistics.getSum());
        System.out.println("平均年龄:" + ageStatistics.getAverage());
        System.out.println("员工总数:" + ageStatistics.getCount());

BigDecimal类型的统计

reduce是一个终结操作,它能够通过某一个方法,对元素进行削减操作。
该操作的结果会放在一个Optional变量里返回。
可以利用它来实现很多聚合方法比如count,max,min等。  
T reduce(T identity, BinaryOperator accumulator); 
第一个参数是我们给出的初值,第二个参数是累加器,可以自己用实现接口完 成想要的操作,这里使用Bigdecimal的add方法 最后reduce会返回计算后的结果  
 BigDecimal result2 = list.stream().map(User::getAnnualIncome)
                .filter(Objects::nonNull).reduce(BigDecimal.ZERO,BigDecimal::add);
System.out.println(result2);

排序方法

sorted() / sorted((T, T) -> int)

空值处理
List<CczgResult> collect = list.stream()
        .sorted(Comparator.comparing(CczgResult::getName2)
                .thenComparing(CczgResult::getName4, Comparator.nullsFirst(String::compareTo))
                .thenComparing(CczgResult::getName6, Comparator.nullsFirst(String::compareTo))
        ).collect(Collectors.toList());

字符串 转数值排序

 rwjdDtos.stream().sorted(Comparator.comparing(RwjdDto::getTpcode, Comparator.comparingInt(Integer::parseInt))) .collect(Collectors.toList());
list.stream().sorted((o1, o2)->o1.getItem().getValue().compareTo(o2.getItem().getValue())).
collect(Collectors.toList());
// 对象集合以类属性一升序排序
list.stream().sorted(Comparator.comparing(类::属性一));
// 对象集合以类属性一降序排序 注意两种写法

list.stream().sorted(Comparator.comparing(类::属性一).reversed());//先以属性一升序,结果进行属性一降序

list.stream().sorted(Comparator.comparing(类::属性一,Comparator.reverseOrder()));//以属性一降序

// 对象集合以类属性一升序 属性二升序

list.stream().sorted(Comparator.comparing(类::属性一).thenComparing(类::属性二));

// 对象集合以类属性一降序 属性二升序 注意两种写法

list.stream().sorted(Comparator.comparing(类::属性一).reversed().thenComparing(类::属性二));//先以属性一升序,升序结果进行属性一降序,再进行属性二升序

list.stream().sorted(Comparator.comparing(类::属性一,Comparator.reverseOrder()).thenComparing(类::属性二));//先以属性一降序,再进行属性二升序

// 对象集合以类属性一降序 属性二降序 注意两种写法

list.stream().sorted(Comparator.comparing(类::属性一).reversed().thenComparing(类::属性二,Comparator.reverseOrder()));//先以属性一升序,升序结果进行属性一降序,再进行属性二降序

list.stream().sorted(Comparator.comparing(类::属性一,Comparator.reverseOrder()).thenComparing(类::属性二,Comparator.reverseOrder()));//先以属性一降序,再进行属性二降序

// 对象集合以类属性一升序 属性二降序 注意两种写法

list.stream().sorted(Comparator.comparing(类::属性一).reversed().thenComparing(类::属性二).reversed());//先以属性一升序,升序结果进行属性一降序,再进行属性二升序,结果进行属性一降序属性二降序

list.stream().sorted(Comparator.comparing(类::属性一).thenComparing(类::属性二,Comparator.reverseOrder()));//先以属性一升序,再进行属性二降序


  1. Comparator.comparing(类::属性一).reversed(); 得到排序结果后再排序
  2. Comparator.comparing(类::属性一,Comparator.reverseOrder()); 直接进行排序

很多人会混淆导致理解出错,2更好理解,建议使用2

集合转换

list转list
list = list.stream().map(o-> {
        VipInfoSear vipInfoSear = new VipInfoSear();
        vipInfoSear.setId(o.getId());
        return vipInfoSear;
}).collect(Collectors.toList());
List转map
// 一对多转换
Map<String, List<User>> userMapGroupByGender = list.stream().collect(
        Collectors.groupingBy(User::getGender));
// 一对一转换
Map<Integer, User> userMapGroupByUserId = list.stream().collect(
        Collectors.toMap(User::getUserId, o -> o));
// 一对一属性转换        
Map<Integer, String> map = list.stream().collect(
  Collectors.toMap(User::getUserId, User::getName));        

分组

分组 注意空值处理
lisrt.stream()
        .filter(t-> StringUtils.isNotBlank(t.getName12()))
        .collect(Collectors.groupingBy(CczgResult::getName12));
分组多条件统计
Map<String, Long> countMap = records.stream().collect(Collectors.groupingBy(o -> o.getProductType() + "_" + o.getCountry(), Collectors.counting()));

List<Record> countRecords = countMap.keySet().stream().map(key -> {
    String[] temp = key.split("_");
    String productType = temp[0];
    String country = temp[1];
    
    Record record = new Record();
    record.set("device_type", productType);
    record.set("location", country;
    record.set("count", countMap.get(key).intValue());
    return record;
}).collect(Collectors.toList());

是否存在重复字段的数据

List<String> list2 = list.stream()
                        .map(User::getGender)
                        .collect(Collectors.toMap(e -> e, e -> 1, Integer::sum))
                        .entrySet()
                        .stream()
                        .filter(entry -> entry.getValue() > 1)
                        .map(Map.Entry::getKey)
                        .collect(Collectors.toList());
                        
CollectionUtils.isNotEmpty(list2 );


未完持续更新中~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值