Java集合Stream流操作的基本使用教程分享

作者:码指星斗 时间:2023-09-01 01:05:03 

Java 中可以使用 java.util.Stream 对一个集合(实现了java.util.Collection接口的类)做各种操作,例如:求和、过滤、排序等等。

这些操作可能是中间操作——返回一个 Stream 流,或者是终端操作——返回一个结果。

流操作并不会影响原来的集合,可以简单认为,流操作是把集合中的一个元素逐个复制放到一个首尾相接的流动的水槽中。

Stream 流支持同步执行,也支持并发执行。如果我们直接获取 stream 流,得到的是同步执行的 stream 流;如果调用方法 parallelStream,则得到一个可以并发执行的 Stream 流。

注意:Map不支持 Stream 流,但是它的 KeyValue 支持,因为它们实现了 Set 接口。

事前准备

演示 Stream 流的提前准备,创建几个类以供测试

  • 新建一个工具类,方便创建集合。

  • 新建两个类,例如开发中常见的数据库实体类和 DTO 类。

public class MyUtil {

    private static List<String> list = new ArrayList<>();
    private static List<Student> students = new ArrayList<>();

    static {
        list.add("abc");
        list.add("xyz");
        list.add("fgh");
        list.add("abc");
        list.add("def");
        list.add("xyz");
        list.add("efg");

        Student s1 = new Student();
        s1.setAge("16");
        s1.setId(UUID.randomUUID().toString());
        s1.setName("张三");
        s1.setMajor("计算机科学与技术");
        Student s2 = new Student();
        s2.setAge("18");
        s2.setId(UUID.randomUUID().toString());
        s2.setName("李四");
        s2.setMajor("物联网工程");
        Student s3 = new Student();
        s3.setAge("20");
        s3.setId(UUID.randomUUID().toString());
        s3.setName("王五");
        s3.setMajor("网络工程");
        students.add(s1);
        students.add(s2);
        students.add(s3);
    }

    public static List<String> getList() {
        return list;
    }
    public static List<Student> getStudents() {
        return students;
    }
}

public class Student {

    private String id;
    private String name;
    private String age;
    private String major;

}

public class StudentDTO {

    private String name;
    private String major;
}

Filter

filter 可以帮助我们过滤流中的某些元素,其方法签名如下

/*
过滤操作,
Predicate 相当于一个谓词,即断言流中的元素满足某个条件,返回一个 布尔值
*/
Stream<T> filter(Predicate<? super T> predicate);

具体使用方法如下:

public class Main {

    public static void main(String[] args) {
        List<String> list = MyUtil.getList();
        System.out.println("过滤操作之前:");
        System.out.println(list);
        // 过滤不以 a 开头的字符串,collect() 将流中的元素放到一个新的集合中
        List<String> newList = list.stream().filter(s -> !s.startsWith("a")).collect(Collectors.toList());
        System.out.println("-------------------------");
        System.out.println("过滤操作之后:");
        System.out.println(newList);
    }
}

======== 输出 =========
过滤操作之前:
[abc, xyz, fgh, abc, def, xyz, efg]
-------------------------
过滤操作之后:
[xyz, fgh, def, xyz, efg]

Sorted

sorted 可以帮助我们排序流中的元素,方法签名如下:

/*
中间操作,传入一个 Comparator,对流中的元素进行排序,如果不传入,则使用默认的 Comparable 排序
对原集合不影响
*/
Stream<T> sorted(Comparator<? super T> comparator);

具体使用方法如下:

public class Main {

    public static void main(String[] args) {
        List<String> list = MyUtil.getList();
        System.out.println("排序操作之前:");
        System.out.println(list);
        List<String> newList = list.stream().sorted().collect(Collectors.toList());
        System.out.println("-------------------------");
        System.out.println("排序操作之后:");
        System.out.println(newList);
        System.out.println("自定义排序:");
        // 倒序排序。 forEach 方法可以用传入的方法 逐个 处理流中的元素
        list.stream().sorted((s1, s2)-> -s1.compareTo(s2)).forEach(System.out::println);
    }
}

======== 输出 =========
排序操作之前:
[abc, xyz, fgh, abc, def, xyz, efg]
-------------------------
排序操作之后:
[abc, abc, def, efg, fgh, xyz, xyz]
自定义排序:
xyz
xyz
fgh
efg
def
abc
abc

Map

Map 操作可以帮助我们将流中的一类元素映射为另一类元素,最典型的应用就是可以用来将数据库实体类转换为供前端使用的 DTO 类。方法签名如下:

/*
中间操作,可以将一个对象转化为另一个对象
例如做 DTO 数据转换
*/
<R> Stream<R> map(Function<? super T, ? extends R> mapper);

具体使用方法如下:

public class Main {

    public static void main(String[] args) {
        List<Student> students = MyUtil.getStudents();
        System.out.println("map 操作之前");
        System.out.println(students);
        // collect 方法可以将流中的元素收集到一个 Collection 中,如果有去除重复元素的需求,可以考虑收集到 Set 中
        List<StudentDTO> dtos = students.stream().map(student -> {
            StudentDTO dto = new StudentDTO();
            dto.setName(student.getName());
            dto.setMajor(student.getMajor());
            return dto;
        }).collect(Collectors.toList());
        System.out.println("-------------------------");
        System.out.println("map 操作之后");
        System.out.println(dtos);
    }
}

======== 输出 =========
map 操作之前
[Student{id='cb5726cd-e73a-443e-95e5-155aa6e876ae', name='张三', age='16', major='计算机科学与技术'}, Student{id='94478bae-b2ee-4c43-bac0-12f45f4099cd', name='李四', age='18', major='物联网工程'}, Student{id='5fdd9e19-f7cf-4c61-b506-0ef58a36dcbe', name='王五', age='20', major='网络工程'}]
-------------------------
map 操作之后
[StudentDTO{name='张三', major='计算机科学与技术'}, StudentDTO{name='李四', major='物联网工程'}, StudentDTO{name='王五', major='网络工程'}]

Match

/*
终端操作,可以用来匹配操作,返回一个 boolean 值
可以方便地匹配集合中是否存在某种元素
*/
// 只要集合中有一个匹配,就返回 true
boolean anyMatch(Predicate<? super T> predicate);
// 集合中所有元素都匹配,才返回 true
boolean allMatch(Predicate<? super T> predicate);
// 集合中所有元素都不匹配,返回 true
boolean noneMatch(Predicate<? super T> predicate);

具体使用方法如下:

public class Main {

    public static void main(String[] args) {
        List<String> list = MyUtil.getList();
        System.out.println("集合中的所有元素是否都以 a 开头");
        System.out.println(list.stream().allMatch(s -> s.startsWith("a")));

        System.out.println("集合中是否存在元素以 a 开头");
        System.out.println(list.stream().anyMatch(s -> s.startsWith("a")));

        System.out.println("集合中的元素是否都不以 a 开头(相当于 allMatch 的取反):");
        System.out.println(list.stream().noneMatch(s -> s.startsWith("a")));
    }
}

======== 输出 =========
集合中的所有元素是否都以 a 开头
false
集合中是否存在元素以 a 开头
true
集合中的元素是否都不以 a 开头(相当于 allMatch 的取反):
false

Count

/*
终端操作,返回 stream 流中及集合中的元素个数,返回一个 long 类型
*/
long count();

具体使用方法如下:

public class Main {

    public static void main(String[] args) {
        List<String> list = MyUtil.getList();
        System.out.println(list);
        System.out.println("集合中的个数:" + list.size());

        long count = list.stream().filter(s -> s.startsWith("a")).count();
        System.out.println("集合中以 a 开头的元素个数:" + count);
    }
}

======== 输出 =========
[abc, xyz, fgh, abc, def, xyz, efg]
集合中的个数:7
集合中以 a 开头的元素个数:2

Reduce

/*
终端操作,可以理解为减少集合的个数,对集合中的元素不断进行累加,最终只得到一个元素
Optional 包含一个对象,可以防止空指针异常
*/
Optional<T> reduce(BinaryOperator<T> accumulator);

具体使用方法如下:

public class Main {

    public static void main(String[] args) {
        List<String> list = MyUtil.getList();
        // 可以理解为减少集合的个数,对集合中的元素不断进行累加,最终只得到一个元素
        // 例如对数字集合进行累加进行求和
        String s = list.stream().reduce((s1, s2) -> s1 + "###" + s2).get();
        System.out.println(s);
    }
}

======== 输出 =========
abc###xyz###fgh###abc###def###xyz###efg

来源:https://juejin.cn/post/7202153876438925372

标签:Java,Stream流,操作
0
投稿

猜你喜欢

  • Android 图片处理避免出现oom的方法详解

    2023-09-07 07:26:12
  • 解析Android截取手机屏幕两种实现方案

    2023-12-16 21:28:06
  • Java8中CompletableFuture的用法全解

    2023-09-08 15:08:55
  • OpenGL Shader实现光照发光体特效

    2022-03-16 18:51:50
  • SpringBoot路径映射实现过程图解

    2023-11-13 04:01:11
  • Android RecyclerView item选中放大被遮挡问题详解

    2023-04-11 17:58:31
  • 解析C#中[],List,Array,ArrayList的区别及应用

    2022-07-29 16:24:10
  • logback之自定义指定日志文件存储目录方式

    2022-05-11 15:05:19
  • java rocketmq--消息的产生(普通消息)

    2023-10-19 08:51:50
  • C#获取路径的几种方式实例分析

    2023-07-18 22:19:16
  • Android自定义view仿淘宝快递物流信息时间轴

    2022-02-20 18:51:16
  • 关于Java虚拟机HotSpot

    2022-10-28 18:36:01
  • 千万不要被阶乘吓倒

    2021-10-06 22:35:45
  • Java计算文本MD5加密值的方法示例

    2023-11-15 13:18:48
  • 详细了解C语言二叉树的建立与遍历

    2021-08-17 10:24:01
  • Java Swing实现扫雷源码

    2023-11-10 08:16:20
  • 详解java 中泛型中的类型擦除和桥方法

    2021-07-10 23:55:08
  • Maven项目修改JDK版本全过程

    2021-07-19 12:13:29
  • AndroidStudio项目打包成jar的简单方法

    2023-07-07 05:33:27
  • C语言实现贪吃蛇游戏演示

    2023-07-03 14:16:43
  • asp之家 软件编程 m.aspxhome.com