JDK8 新特性, stream流用起来有多爽 (结合案例详解--通透--讲清)

简介: 这是一期关于JDK8 新特性 Stream API 进阶使用的文章, 来帮助咱们经常写crud的朋友来简化开发, 可能你刚开始用的时候很抵触, 但是你用熟练了会觉得它真的爽.希望大家多多支持

👳我亲爱的各位大佬们好😘😘😘
♨️本篇文章记录的为 JDK8 新特性 Stream API 进阶 相关内容,适合在学Java的小白,帮助新手快速上手,也适合复习中,面试中的大佬🙉🙉🙉。
♨️如果文章有什么需要改进的地方还请大佬不吝赐教❤️🧡💛
👨‍🔧 个人主页 : 阿千弟
🔥 相关内容👉👉👉 : 都2023年了,如果不会Lambda表达式、函数式编程?你确定能看懂公司代码?

前情回顾 :

最近, 我的一些已经入职一段时间的朋友和我说, 在公司中做的最多的业务就是crud, 他最近做的项目是一个h5的中移动一个外包项目, 虽然业务并不复杂, 但是呢数据量非常庞大, 数据库的表很多, 大概四五百张表, 每天的crud写的我这哥们也是很烦.😭😭😭

所以对于这个问题, 建议哥们可以尝试使用 JDK8 新特性 Stream的方式来处理数据, 对于集合, 数组, map的处理很方便, 也很快速, 这时一些朋友会问, stream阅读性不好, 难以读懂, 但是呢你想想别管它咋样, 能有利于咱们开发提高咱们的开发效率就行了呗

为此我专门出来一期关于JDK8 新特性 Stream API 进阶使用的文章, 来帮助咱们经常写crud的朋友来简化开发, 可能你刚开始用的时候很抵触, 但是你用熟练了会觉得它真的爽.🙏🙏🙏希望大家多多支持

在这里插入图片描述

1、了解 Stream

​ Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一个则是Stream API(java.util.stream.*)。Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。

2、什么是 Stream

流(Stream) 到底是什么呢?

是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。“集合讲的是数据,流讲的是计算!”

注意:

  • Stream 自己不会存储元素。
  • Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
  • Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

    3、Stream 的操作三个步骤

  • 创建 Stream

一个数据源(如:集合、数组),获取一个流

  • 中间操作

一个中间操作链,对数据源的数据进行处理

  • 终止操作(终端操作)

一个终止操作,执行中间操作链,并产生结果

Stream 的中间操作

​ 多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”。

1、筛选与切片

方 法| 描 述
|--|--|
filter(Predicate p) | 接收 Lambda , 从流中排除某些元素。
distinct() | 筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
limit(long maxSize) | 截断流,使其元素不超过给定数量。
skip(long n) | 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补

2、映射

方 法 | 描 述
|--|--|
map(Function f)| 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
mapToDouble(ToDoubleFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 DoubleStream。
mapToInt(ToIntFunction f)| 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream。
mapToLong(ToLongFunction f)| 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 LongStream。
flatMap(Function f)| 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流

3、排序

方 法 | 描 述
|--|--|
sorted() | 产生一个新流,其中按自然顺序排序
sorted(Comparator comp) | 产生一个新流,其中按比较器顺序排序

Stream 的终止操作

终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是 void。

1、查找与匹配

方 法 | 描 述
|--|--|
allMatch(Predicate p) |检查是否匹配所有元素
anyMatch(Predicate p) |检查是否至少匹配一个元素
noneMatch(Predicate p)| 检查是否没有匹配所有元素
findFirst() |返回第一个元素
findAny() |返回当前流中的任意元素
count() |返回流中元素总数
max(Comparator c) |返回流中最大值
min(Comparator c) |返回流中最小值
forEach(Consumer c) |内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。相反,Stream API 使用内部迭代——它帮你把迭代做了)

2、归约

方 法 | 描 述
|--|--|
reduce(T iden, BinaryOperator b)| 可以将流中元素反复结合起来,得到一个值。返回 T
reduce(BinaryOperator b) |可以将流中元素反复结合起来,得到一个值。返回 Optional

3、收集

方 法 | 描 述
|--|--|
collect(Collector c) |将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法

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

方法| 返回类型 |作用 |代码
|--|--|--|--|
toList|List |把流中元素收集到List |List emps= list.stream().collect(Collectors.toList());
toSet| Set |把流中元素收集到Set |Set emps= list.stream().collect(Collectors.toSet());
toCollection|Collection| 把流中元素收集到创建的集合 |Collectionemps=list.stream().collect(Collectors.toCollection(ArrayList::new));
counting| Long |计算流中元素的个数| long count = list.stream().collect(Collectors.counting());
summingInt| Integer |对流中元素的整数属性求和| int total=list.stream().collect(Collectors.summingInt(Employee::getSalary));
averagingInt| Double| 计算流中元素Integer属性的平均值 |double avg= list.stream().collect(Collectors.averagingInt(Employee::getSalary));
summarizingInt| IntSummaryStatistics |收集流中Integer属性的和。 |IntSummaryStatisticsiss= list.stream().collect(Collectors.summarizingInt(Employee::getSalary));
joining |String |连接流中每个字符串 |String str= list.stream().map(Employee::getName).collect(Collectors.joining());
maxBy |Optional| 根据比较器选择最大值 |Optionalmax= list.stream().collect(Collectors.maxBy(comparingInt(Employee::getSalary)));
minBy |Optional |根据比较器选择最小值 |Optional min = list.stream().collect(Collectors.minBy(comparingInt(Employee::getSalary)));
reducing |归约产生的类型 |从一个作为累加器的初始值开始,利用BinaryOperator与流中元素逐个结合,从而归约成单个值| int total=list.stream().collect(Collectors.reducing(0, Employee::getSalar, Integer::sum));
collectingAndThen |转换函数返回的类型 |包裹另一个收集器,对其结果转换函数 |int how= list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size));
groupingBy |Map> |根据某属性值对流分组,属性为K,结果为V |Map> map= list.stream().collect(Collectors.groupingBy(Employee::getStatus));
partitioningBy |Map> |根据true或false进行分区 |Map>vd= list.stream().collect(Collectors.partitioningBy(Employee::getManage));

在这里插入图片描述

下面是工作中经常被用到的案例, 请仔细品味

一些使用场景

1、取出自身重复的数据

@Test
public void test01() {
   
   
    List<String> list = Arrays.asList(
        "g", "k", "f", "k", "g", "b"
    );
    List<String> li = list.stream().
            filter(li1 -> list.stream().filter((li2 -> li2.equals(li1))).count() > 1l)
            .collect(toList());
    System.out.println(li);
}

2、累加数字

@Test
public void test12() {
   
   
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    Integer sum = list.stream()
        .reduce(0, (x, y) -> x + y);
    System.out.println(sum);
}

3、字符串数字集合与整数集合互转

@Test
public void test14() {
   
   
    List<Integer> intList = new ArrayList<>();
    intList.add(1);
    intList.add(3);
    intList.add(4);
    List<String> intToStrList = intList.stream().map(String::valueOf).collect(toList());
    intToStrList.stream().forEach(str -> {
   
   
        System.out.println(str);
    });

    List<String> stringList = new ArrayList<>();
    stringList.add("10");
    stringList.add("20");
    stringList.add("30");
    List<String> stringToIntList = stringList.stream().map(String::valueOf).collect(toList());
    stringToIntList.stream().forEach(num -> {
   
   
        System.out.println(num);
    });
}

4、取list1与list2的交集

List<String> list1 = Arrays.asList(
    "张三", "李四", "李刚", "隋通"
);

List<String> list2 = Arrays.asList(
    "熊大", "熊二", "张三", "李刚"
);

//15.取list1与list2的交集
@Test
public void test15() {
   
   
    List<String> intersection = list1.stream().filter(item -> list2.contains(item)).collect(toList());
    System.out.println("---得到交集---");
    intersection.parallelStream().forEach(System.out::println);
}

5、取list1与list2的差集

@Test
public void test16() {
   
   
    List<String> reduce1 = list1.stream().filter(item -> !list2.contains(item)).collect(toList());
    System.out.println("---得到差集 reduce1 (list1 - list2)---");
    reduce1.parallelStream().forEach(System.out::println);
}

6、取list1和list2的并集

@Test
public void test18() {
   
   
    List<String> listAll = list1.parallelStream().collect(toList());
    List<String> listAll2 = list2.parallelStream().collect(toList());
    listAll.addAll(listAll2);
    System.out.println("---得到并集 listAll---");
    listAll.parallelStream().forEach(System.out::println);
    // 去重并集
    List<String> listAllDistinct = listAll.stream().distinct().collect(toList());
    System.out.println("---得到去重并集 listAllDistinct---");
    listAllDistinct.parallelStream().forEach(System.out::println);
    System.out.println("---原来的List1---");
    list1.parallelStream().forEach(System.out::println);
    System.out.println("---原来的List2---");
    list2.parallelStream().forEach(System.out::println);
}

7、统计集合重复元素出现次数,并且去重返回hashmap

List<String> list = Arrays.asList(
    "b", "g", "d", "d", "t", "j", "d", "a", "c", "f", "a", "b", "s", "w", "w", "b"
);

//19.统计集合重复元素出现次数,并且去重返回hashmap
@Test
public void test19() {
   
   
    Map<String, Long> map = list.stream().
        collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
    System.out.println(map);
    //由于hashmap无序,所以在排序放入LinkedHashMap里(key升序)
    Map<String, Long> sortMap = new LinkedHashMap<>();
    map.entrySet().stream().sorted(Map.Entry.comparingByKey()).
        forEachOrdered(e -> sortMap.put(e.getKey(), e.getValue()));
    System.out.println(sortMap);
    //获取排序后map的key集合
    List<String> keys = new LinkedList<>();
    sortMap.entrySet().stream().forEachOrdered(e -> keys.add(e.getKey()));
    System.out.println(keys);
    //获取排序后map的value集合
    List<Long> values = new LinkedList<>();
    sortMap.entrySet().stream().forEachOrdered(e -> values.add(e.getValue()));
    System.out.println(values);
}

8、stream循环拿到下标

@Test
public void test21(){
   
   
    Stream.iterate(0, i -> i + 1).limit(users01.size()).forEach(index -> {
   
   
        System.out.println(index);
    });
}

9、List集合转Map任意一个属性作为键,键相同的对象用集合存储

@Test
public void test22(){
   
   
    Map<Integer,List<User>> userMap = new HashMap<>();
    List<User> userList = null;
    for (int i = 0; i < users02.size(); i++) {
   
   
        User user = users02.get(i);
        if(userMap.get(user.getAge())==null){
   
   
            userList = new ArrayList<>();
        }
        userList.add(user);
        userMap.put(user.getAge(),userList);
    }
    for(Integer key : userMap.keySet()){
   
   
        System.out.println("key:"+key+"\n"+"value:"+userMap.get(key));
    }
}

10、List集合转Map任意一个属性作为键,键相同的对象用集合存储(Java8新特性实现)

@Test
public void test23(){
   
   
    Map<Integer, List<User>> listMap = users02.stream().collect(Collectors.groupingBy(User::getAge));
    listMap.keySet().forEach(key->{
   
   
        System.out.println(key);
        listMap.get(key).forEach((user)->System.out.println(user));
    });
    for(Integer key : listMap.keySet()){
   
   
        System.out.println("key:"+key+"\n"+"value:"+listMap.get(key));
    }
}

11、取出users01中age一样的,并算出有几项

@Test
public void test03() {
   
   
    List<User> userList = users01.stream().filter(user1 -> users01.stream().filter((user2 -> user2.getAge().equals(user1.getAge()))).count() > 1l)
        .collect(toList());

    userList.stream().forEach((user) -> {
   
   
        System.out.println(user.getName() + ":" + user.getAge());
    });
    Map<Object, Object> str = new HashMap<>();
    for (User user : userList) {
   
   
        List<User> collect = userList.stream().filter(d -> d.getAge().equals(user.getAge())).collect(toList());
        str.put(user.getAge(), collect.size());
    }
    for (Object key : str.keySet()) {
   
   
        String value = str.get(key).toString();//
        System.out.println("key:" + key + " value:" + value);
    }
}

12、取出users01中的Id在users02中没有的user

/**
     * allMatch——检查是否匹配所有元素
     * anyMatch——检查是否至少匹配一个元素
     * noneMatch——检查是否没有匹配的元素
     */
//4.取出users01中的Id在users02中没有的user
@Test
public void test04() {
   
   
    //取出所有users02的id
    List<Integer> user02Ids = users02.stream().map(User::getId).collect(toList());
    System.out.println(user02Ids);
    List<User> userList = users01.stream().filter(user1 -> !user02Ids.stream().anyMatch(user02Id -> user1.getId().equals(user02Id))).collect(toList());
    userList.stream().forEach((user) -> {
   
   
        System.out.println(user.getName() + ":" + user.getAge());
    });



    List<SkuEsModel.Attrs> attrList = baseAttrs.stream().filter(item -> {
   
   
        // 过滤掉不包含在 searchAttrIds集合中的元素
        return idSet.contains(item.getAttrId());
    }).collect(Collectors.toList());
}

13、去掉users01name重复的只保留一个

@Test
public void test07() {
   
   
    List<User> userList = users01.stream().collect(
        Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(o -> o.getName()))),
                                     ArrayList::new));
    userList.stream().forEach((user) -> {
   
   
        System.out.println(user.getName() + ":" + user.getAge() + ":" + user.getBirthday());
    });
}

14、将Student信息注入到相应的Grade里面的studentList中

List<Student> stuList = Arrays.asList(
    new Student(1, "张三", 1),
    new Student(2, "李四", 2),
    new Student(3, "李飞", 2)
);

List<Grade> gradeList = Arrays.asList(
    new Grade(1, "大一"),
    new Grade(2, "大二")
);

//13.将Student信息注入到相应的Grade里面的studentList中
@Test
public void test13() {
   
   
    gradeList.stream().forEach((grade) -> {
   
   
        List<Student> students = stuList.stream().filter((stu) -> stu.getGradeId().equals(grade.getGradeId())).collect(toList());
        grade.setStudentList(students);
    });
    gradeList.stream().forEach(grade -> {
   
   
        System.out.println(grade.getGradeName() + "---");
        grade.getStudentList().stream().forEach(stu -> {
   
   
            System.out.println(stu.getStuName());
        });
    });
}

在这里插入图片描述

如果这篇【文章】有帮助到你💖,希望可以给我点个赞👍,创作不易,如果有对Java后端或者对spring, 分布式, 云原生感兴趣的朋友,请多多关注💖💖💖
👨‍🔧 个人主页 : 阿千弟

目录
相关文章
|
2月前
|
Oracle Java 关系型数据库
CentOS 7.6操作系统部署JDK实战案例
这篇文章介绍了在CentOS 7.6操作系统上通过多种方式部署JDK的详细步骤,包括使用yum安装openjdk、基于rpm包和二进制包安装Oracle JDK,并提供了配置环境变量的方法。
265 80
|
2月前
|
容器
jdk8新特性-详情查看文档
jdk8新特性-详情查看文档
45 3
|
1月前
|
存储 安全 Java
JDK1.8 新的特性
JDK1.8 新的特性
19 0
|
2月前
|
编解码 安全 Java
jdk8新特性-接口和日期处理
jdk8新特性-接口和日期处理
|
3月前
|
API
JDK8的stream有求和方法吗?
【8月更文挑战第20天】JDK8的stream有求和方法吗?
123 3
|
3月前
|
Java API
JDK8到JDK25版本升级的新特性问题之使用Collectors.teeing()来计算一个列表中学生的平均分和总分如何操作
JDK8到JDK25版本升级的新特性问题之使用Collectors.teeing()来计算一个列表中学生的平均分和总分如何操作
|
3月前
|
Java API Apache
JDK8到JDK24版本升级的新特性问题之在Java中,HttpURLConnection有什么局限性,如何解决
JDK8到JDK24版本升级的新特性问题之在Java中,HttpURLConnection有什么局限性,如何解决
|
3月前
|
Oracle Java 关系型数据库
JDK8到JDK29版本升级的新特性问题之未来JDK的升级是否会成为必然趋势,如何理解
JDK8到JDK29版本升级的新特性问题之未来JDK的升级是否会成为必然趋势,如何理解
|
3月前
|
Oracle 安全 Java
JDK8到JDK28版本升级的新特性问题之在Java 15及以后的版本中,密封类和密封接口是怎么工作的
JDK8到JDK28版本升级的新特性问题之在Java 15及以后的版本中,密封类和密封接口是怎么工作的
|
3月前
|
Java API 开发者
JDK8到JDK17版本升级的新特性问题之SpringBoot选择JDK17作为最小支持的Java lts版本意味着什么
JDK8到JDK17版本升级的新特性问题之SpringBoot选择JDK17作为最小支持的Java lts版本意味着什么
125 0
JDK8到JDK17版本升级的新特性问题之SpringBoot选择JDK17作为最小支持的Java lts版本意味着什么