JavaEE之Stream流

Stream流体验

  • 案例需求: 有一个List集合,元素有"张三丰","张无忌","周芷若","赵敏","张强",找出姓张,且是3个字的名字,存入到一个新集合中去。

集合玩法

java 复制代码
public class StreamDemo1 {
    public static void main(String[] args) {
        // 目标:认识Stream流,掌握其基本使用步骤。体会它的优势和特点。
        List<String> list = new ArrayList<>();
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张强");
        list.add("张三丰");
        list.add("张翠山");
        // 1、先用传统方案:找出姓张的人,名字为3个字的,存入到一个新集合中去。
        List<String> newList = new ArrayList<>();
        for (String name : list) {
            if(name.startsWith("张") && name.length() == 3){
                newList.add(name);
            }
        }
        System.out.println(newList);
    }
}

Stream流玩法

  • 用Stream流来做,代码是这样的(ps: 是不是像流水线一样,一句话就写完了)
scss 复制代码
// 2、使用Stream流解决
List<String> newList2 = list.stream().filter(
    s -> s.startsWith("张")).filter(
        s -> s.length() == 3).collect(Collectors.toList());
System.out.println(newList2);

先不用知道这里面每一句话是什么意思,具体每一句话的含义,待会再一步步学习。现在只是体验一下。 学习Stream流我们接下来,会按照下面的步骤来学习。

Stream流的操作步骤

获取Stream流

  • 接下来我们正式来学习Stream流。先来学习如何创建Stream流、或者叫获取Stream流。
  • 主要掌握下面三点:
    1. 如何获取单列集合List集合的Stream流?
    2. 如何获取Map集合的Stream流?
    3. 如何获取数组的Stream流?

语法

  • 获取集合的Stream流

    Collection提供的如下方法 说明
    default Stream stream() 获取当前集合对象的Stream流
    • 注意:map无法直接获取流,需要转换为单列集合再获取,如Entry对象
  • 获取数组的Stream流(两种方式)

    Arrays类提供的如下 方法 说明
    public static Stream stream(T[] array) 获取当前数组的Stream流
    Stream类提供的如下 方法 说明
    public static Stream of(T... values) 获取当前接收数据的Stream流
  • 代码演示

    java 复制代码
    import java.util.*;
    import java.util.stream.Stream;
    
    
    // 目标:获取stream流。
    // 1、单列集合获取流的方法都是:调用stream()方法
    // 2、Map集合获取stream()流。
    // 创建map集合
    // 获取map集合的键流
    // 获取map集合的值流
    // 获取map集合的键值对对象流
    // 3、获取数组的流。
    //准备数组
    String[] arr = {"张三丰", "张无忌", "赵敏", "周芷若"};
    //方式1:Arrays.stream(数组)
    //方式2:Stream.of(数组)
    public class Demo012 {
        public static void main(String[] args) {
            // 目标:获取stream流。
            // 1、单列集合获取流的方法都是:调用stream()方法
            List<String> list = new ArrayList<>();
            list.add("张无忌");
            list.add("周芷若");
            list.add("赵敏");
            list.add("张强");
            list.add("张三丰");
            Stream<String> stream1 = list.stream();
            Stream<Object> stream2 = new HashSet<>().stream();
            Stream<Object> stream3 = new LinkedHashSet<>().stream();
            Stream<Object> stream4 = new LinkedList<>().stream();
    
            // 2、Map集合获取stream()流。(注意:map无法直接获取流,需要转换为单列集合再获取)
             // 创建map集合
            Map<String,String> map = new HashMap<>();
            // 获取map集合的键流
            Stream<String> stream5 = map.keySet().stream();
            // 获取map集合的值流
            Stream<String> stream6 = map.values().stream();
           // 获取map集合的键值对对象流
            Stream<Map.Entry<String, String>> stream7 = map.entrySet().stream();
             // 3、获取数组的流。
             //准备数组
            String[] arr = {"张三丰", "张无忌", "赵敏", "周芷若"};
                    //方式1:Arrays.stream(数组)
            Stream<String> stream8 = Arrays.stream(arr);
                    //方式2:Stream.of(数组)
            Stream<String> stream9 = Stream.of(arr);
        }
    }

可变参数

  • 就是一种特殊形参,定义在方法、构造器的形参列表里,格式是:数据类型...参数名称;

  • 可变参数的特点和好处

    • 特点:可以不传数据给它;可以传一个或者同时传多个数据给它;也可以传一个数组给它。
    • 好处:常常用来灵活的接收数据。
  • 可变参数的注意事项:

    • 可变参数在方法内部就是一个数组。
    • 可变参数必须放在形参列表的最后面(写在前面传值时无法分辨从第几个开始不是可变参数)
    • 一个形参列表中可变参数只能有一个(多个传值时无法分辨从哪里开始是下一个可变参数的值)
  • 代码演示

    java 复制代码
    public static void main(String[] args) {
        //目标:调用可变参数方法
        //作用:传递参数灵活
        //传递1个参数
        demo("hello");
        //传递多个参数
        demo("hello","a","b","c");
        //传递一个数组
        demo(new String[]{"a","b","c"});
    }
    
    
    public static void demo(String... str){
        //str本质是一个数组
        System.out.println("str数组内存地址:"+str);
            System.out.println("str数组内存地址:"+ Arrays.toString(str));
            System.out.println("str数组元素的个数:"+ str.length);
            System.out.println("------------------------------------");
        }
    
        // public static void demo2(String... str,Integer abc){ 
        //错误,可变参数必须放到参数列表最后
        //     //str本质一个数组
        //     System.out.println(Arrays.toString(str));
        //     System.out.println(str.length);
        //     System.out.println("----------------------");
        // }
    
        // public static void demo2(String... str,Integer... abc){ //错误,可变参数最多只有一个
        //     //str本质一个数组
        //     System.out.println(Arrays.toString(str));
        //     System.out.println(str.length);
        //     System.out.println("----------------------");
        // } 
  • 通过段点也可以看到,本质上是数组

Stream流中间方法

  • 学习了创建Stream流的方法。接下来我们再来学习,Stream流中间操作的方法。

  • 中间方法指的是:调用完方法之后其结果是一个新的Stream流,于是可以继续调用方法,这样一来就可以支持链式编程(流式编程)

    Stream提供的常用中间方法 说明
    Stream filter(Predicate<? super T> predicate) 用于对流中的数据进行过滤。
    Stream<T> sorted() 对元素进行升序排序
    Stream<T> sorted (Comparator<? super T> comparator) 按照指定规则排序
    Stream limit (long maxSize) 获取前几个元素
    Stream skip (long n) 跳过前几个元素
    Stream distinct () 去除流中重复的元素。(对象需重写hasCode和equals方法)
    Stream map (Function<? super T, ? extends R> mapper) 对元素进行加工,并返回对应的新流
    static Stream concat (Stream a, Stream b) 合并a和b两个流为一个流
    java 复制代码
    import java.util.ArrayList;
    import java.util.List;
    import java.util.function.Function;
    import java.util.stream.Stream;
    public class Demo014 {
        public static void main(String[] args) {
            // 目标:搞清楚Stream流提供的中间方法。
            List<String> list = new ArrayList<>();
            list.add("张无忌");
            list.add("周芷若");
            list.add("赵敏");
            list.add("张强");
            list.add("张三丰");
            list.add("张无忌");
            // 1、过滤:筛选数据的,查找姓"张",并遍历
            list.stream().filter(name->name.startsWith("张")).forEach(System.out::println);
            System.out.println("--------------");
            // 2、排序的:sorted 默认是升序。
            list.stream().sorted().forEach(System.out::println);
            System.out.println("--------------");
            // 3、集合中是对象的排序(要么对象实现Comparable接口,要么重写比较方法)
            List<Student> students = new ArrayList<>();
            students.add(new Student("小明", 18, 90.0, "北京"));
            students.add(new Student("小明", 18, 90.0, "北京"));
            students.add(new Student("小花", 21, 60.0, "深圳"));
            students.add(new Student("小红", 19, 80.0, "上海"));
            students.add(new Student("小刚", 20, 70.0, "广州"));
            //按照年龄升序(实现Comparable接口)
            students.stream().sorted().forEach(System.out::println);
            //这里对于对象类型默认安装student类型里面实现Comparable接口进行排序
            System.out.println("--------------");
            //按照分数降序排序(重写比较方法)
            students.stream().sorted((o1, o2) -> Double.compare(o2.getScore(),o1.getScore())).forEach(System.out::println);
            System.out.println("--------------");
            // 4、limit 取前几个
            list.stream().limit(3).forEach(System.out::println);
            System.out.println("--------------");
            // 5、跳过前几个:skip
            list.stream().skip(3).forEach(System.out::println);
            System.out.println("--------------");
            // 6、加工方法:map
            //map加工后返回元素类型可以与原集合元素类型不一样,返回元素的类型由map里面决定
            students.stream().map(new Function<Student, String>() {
                @Override
                public String apply(Student student) {
                    return student.getName()+","+student.getScore();
                }
            }).forEach(System.out::println);
            // 简写形式:
            students.stream().map(
                    student -> student.getName() + "," + student.getScore()
            ).forEach(System.out::println);
            System.out.println("--------------");
            // 7、合并流:两个流(list集合流,list2集合流)合并成一个流。
            // Stream<String> stream1 = list.stream();
            // Stream<Double> stream2 = list2.stream();
            // Stream<? extends Serializable> stream = Stream.comcat(stream1,stream2);
            // // Serizalizable是list集合的父类,这里的Stream流存放的是集合,所以存放了集合的父类
            Stream.concat(list.stream(),students.stream()).forEach(System.out::println);
            System.out.println("--------------");
            //8、去重 distinct
            list.stream().distinct().forEach(System.out::println);
            System.out.println("--------------");
            students.stream().distinct().forEach(System.out::println); 
            //如果是对象类型会调用对象的hashCode()和equals()去重,因为distinct也是根据equals和hasCode来判断去重的
         }
    }

Stream流的终结方法、收集Stream流

Stream流的终结方法

  • 终结方法指的是调用完成后,不会返回新Stream了,没法继续使用流了。

    Stream提供的常用终结方法 说明
    void forEach(Consumer action) 对此流运算后的元素执行遍历
    long count() 统计此流运算后的元素个数
    Optional<T> max(Comparator<? super T> comparator) 获取此流运算后的最大值元素
    Optional<T> min(Comparator<? super T> comparator) 获取此流运算后的最小值元素
java 复制代码
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Optional;

    public class Demo015 {

        public static void main(String[] args) {
            // 目标:学习Stream流的终结方法。
        List<String> list = new ArrayList<>();
                list.add("张无忌");
                list.add("周芷若");
                list.add("赵敏");
                list.add("张强");
                list.add("张三丰");
                list.add("张无忌");
                // 1、遍历方法
                list.stream().distinct().forEach(System.out::println);
                // 2、统计数据的个数 count()
                System.out.printf("去重后的个数:%d%n",list.stream().distinct().count());
                // 3、挑选出最小值
                Optional<String> optional = list.stream().min(
                    (o1, o2) -> o1.compareTo(o2)); 
                // 字符串对比,用compareTo方法
                //按照升序取第一个
                if(optional.isPresent()){ 
                    //判断是否存在符合要求的结果
                    String min = optional.get(); 
                    //注意:当没有符合的结果这句代码会报错,所以为了安全需要进行optional.isPresent()
                    System.out.println("最小的姓名:"+min);
                }
                List<Student> students = new ArrayList<>();
                students.add(new Student("小明", 18, 90.0, "北京"));
                students.add(new Student("小红", 19, 80.0, "上海"));
                students.add(new Student("小刚", 20, 70.0, "广州"));
                students.add(new Student("小花", 21, 60.0, "深圳"));
                // 4、挑选出最大值
                Optional<Student> optional2 = students.stream().max(
                        (o1, o2) -> Double.compare(o1.getScore(), o2.getScore())); 
                //按照升序取最后一个
                if(optional2.isPresent()){
                    Student student = optional2.get();
                    System.out.println("最大成绩学生:"+student);
                }
        }
    }

收集Stream流

  • 收集Stream流:就是把Stream流操作后的结果转回到集合或者数组中去返回。

    Stream提供的常用终结方法 说明
    R collect​(Collector collector) 把流处理后的结果收集到一个指定的集合中去
    Object[] toArray() 把流处理后的结果收集到一个数组中去
    Collectors工具类提供了具体的收集方式 说明
    public static Collector toList() 把元素收集到List集合中
    public static Collector toSet() 把元素收集到Set集合中
    public static Collector toMap(Function keyMapper , Function valueMapper) 把元素收集到Map集合中
    java 复制代码
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    import java.util.function.Function;
    import java.util.stream.Collectors;
    
    public class Demo016 {
        public static void main(String[] args) {
        // 目标:收集Stream流。把流收集成集合: 流只能用一次
        List<String> list = new ArrayList<>();
                list.add("张无忌");
                list.add("周芷若");
                list.add("赵敏");
                list.add("张强");
                list.add("张三丰");
                list.add("张无忌");
                // 收集List集合
                List<String> list1 = list.stream().distinct().toList();
                List<String> list2 = list.stream().distinct().collect(Collectors.toList());
                // 收集成Set集合
                Set<String> set1 = list.stream().distinct().collect(Collectors.toSet());
                // 收集成Map集合。
                List<Student> students = new ArrayList<>();
                students.add(new Student("小明", 18, 90.0, "北京"));
                students.add(new Student("小红", 19, 80.0, "上海"));
                students.add(new Student("小刚", 20, 70.0, "广州"));
                students.add(new Student("小花", 21, 60.0, "深圳"));
                //  方式1:匿名内部类方式
                Map<String, Double> map = students.stream().collect(Collectors.toMap(
                        new Function<Student, String>() {
                            @Override
                            public String apply(Student student) {
                                return student.getName();
                            }
                        },
                        new Function<Student, Double>() {
                            @Override
                            public Double apply(Student student) {
                                return student.getScore();
                            }
                        }
                ));
                //  方式2:lambda方式
        Map<String, Double> map2 = students.stream().collect(Collectors.toMap(
                        student -> student.getName(),
                        student->student.getScore()
                ));
                //  方式3(推荐):特殊方法引用方式(类名::实例方法,要求:传入的第一个参数是方法体内主调用方法参数,其他参数作为调用方法的参数)
        Map<String, Double> map3 = students.stream().collect(Collectors.toMap(
                        Student::getName, //特殊类型方法引用: 代替的方法要求是第一个参数作为对象,调用getName方法
        Student::getScore
                ));
                System.out.println(map3);
            }
    }
相关推荐
暮年2 小时前
List并发实现-Vector
后端
Rust研习社2 小时前
Rust Copy 特征详解|新手必看!再也不与 Clone 混淆
后端·rust·编程语言
Cache技术分享2 小时前
385. Java IO API - Chmod 示例:模拟 chmod 命令的文件权限更改
前端·后端
星浩AI2 小时前
手把手带你跑通智能体 A2A 实战案例
后端·langchain·agent
j_xxx404_2 小时前
Linux:缓冲区
linux·运维·c++·后端
希望永不加班2 小时前
SpringBoot 中 AOP 实现接口限流
java·spring boot·后端·spring
董董灿是个攻城狮2 小时前
DeepSeek 开始融资,又打了一手新牌
后端
Sam_Deep_Thinking2 小时前
Spring Bean作用域的设计与使用
java·后端·spring