Stream流

创建不可变集合

什么是不可变集合

  • 不可变集合,就是不可被修改的集合。
  • 集合的数据项在创建的时候提供,并且在整个生命周期中都不可以被改变。否则报错。

为什么要创建不可变集合?

  • 如果某个数据不能被修改,把它防御性地拷贝到不可变集合中是个很好的实践。
  • 或者当集合对象被不可信的库调用时,不可变形式是安全的。

如何创建不可变集合?

  • 在List、Set、Map接口中,都存在of方法,可以创建一个不可变的集合。
方法名称 说明
static List of(E...elements) 创建一个具有指定元素的List集合对象
static Set of(E...elements) 创建一个具有指定元素的Set集合对象
static <K,V> Map<K,V> of(E...elements) 创建一个具有指定元素的Map集合对象
public class CollectionDemo {
    public static void main(String[] args) {
        //1.创建一个不可变的List集合
        List<Double> money = List.of(125.5,23.2,431.3,100.0,125.5);
        System.out.println(money.get(1));
        System.out.println(money);

        Set<String> names = Set.of("张三", "李四", "王五");
        System.out.println(names);

        Map<String, Integer> maps = Map.of("张三",1,"老白",2,"老黑",3);
        //maps.put("你爹",22);
        System.out.println(maps);

    }
}
output:
23.2
[125.5, 23.2, 431.3, 100.0, 125.5]
[王五, 张三, 李四]
{老黑=3, 老白=2, 张三=1}
  • 这个集合不能添加,不能删除,不能修改。

Stream流

Stream流的概述

什么是Stream流?

  • 在java 8中,得益于Lambda所带来的函数式编程,引入了一个全新的Stream流概念
  • 目的:用于简化集合和数组操作的API。结合力Lambda表达式

案例:体验Stream流的作用

需求:按照下面的要求完成集合的创建和遍历

  • 创建一个集合存储多个字符串

  • List<String> list = new ArrayList();
    list.add("张无忌");
    list.add("殷素素");
    list.add("赵敏");
    list.add("张尼");
    list.add("张三丰");
    
  • 把集合中的所有以"张"开头的元素存储到一个新的集合

  • 把"张"开头的集合中的长度为3的元素存储到一个新的集合

  • 遍历上一步得到的集合中的元素输出

public class StreamTest {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        Collections.addAll(names, "张三丰", "张无忌", "赵敏", "殷素素", "张口");
        System.out.println(names);
        List<String> zhang = new ArrayList<>();
        for (int i = 0; i < names.size(); i++) {
            String s = names.get(i);
            if (s.startsWith("张")) {
                zhang.add(s);
            }
        }
        System.out.println(zhang);
        List<String> zhagnThree = new ArrayList<>();
        for (int i = 0; i < zhang.size(); i++) {
            String s = zhang.get(i);
            if (s.length() == 3){
                zhagnThree.add(s);
            }
        }
        System.out.println(zhagnThree);

        //使用Stream流
        names.stream().filter(s->s.startsWith("张") && s.length()==3).forEach(s-> System.out.println(s));
        names.stream().filter(s -> s.startsWith("张")).filter(s -> s.length()==3).forEach(s-> System.out.println(s));
    }
output:
[张三丰, 张无忌, 赵敏, 殷素素, 张口]
[张三丰, 张无忌, 张口]
[张三丰, 张无忌]
张三丰
张无忌
张三丰
张无忌

Stream流式思想的核心:

  1. 先得到集合或者数组的Stream流(就是一根传送带)
  2. 把元素放上去
  3. 然后就用这个Stream流简化的API来方便的操作元素

Stream流的获取

Stream流的三类方法

  • 获取Stream流
    • 创建一条流水线,并把数据放到流水线上准备进行操作
  • 中间方法
    • 流水线上的操作。一次操作完毕之后,还可以继续进行其他操作。
  • 终结方法
    • 一个Stream流只能有一个终结方法,是流水线上的最后一个操作。

Stream操作集合或者数组,第一步应该先得到Stream流,然后才能使用流的功能

集合获取Stream 流的方式

  • 可以使用Collection接口中默认的Stream()方法生成流
方法名称 说明
default Stream stream() 获取当前集合对象的Stream()流

数组获取Stream流的方法

方法名称 说明
public static Stream stream(T[] array) 获取当前数组的Stream流
public static Stream of(T... values) 获取当前数组/可变数据的
public class StreamDemo2 {
    public static void main(String[] args) {
        /** ------------------------Collection集合获取流--------------------*/
        Collection<String> list = new ArrayList<>();
        Stream<String> s = list.stream();

        /** ------------------------Map集合获取流--------------------*/
        Map<String,Integer> maps = new HashMap<>();

        //键流
        Stream<String> keyStream = maps.keySet().stream();

        //值流
        Stream<Integer> valueStream = maps.values().stream();

        //键值对流
        Stream<Map.Entry<String, Integer>> keyAndValueStream = maps.entrySet().stream();

        /** ----------------------------数组获取流----------------------------*/
        String[] names = {"甲","不懂","霸占了","属于"};
        //方法一
        Stream<String> namesStream = Arrays.stream(names);
        //方法二
        Stream<String> nameStream = Stream.of(names);
    }
}

Stream流的常用API

Stream流的常用API(中间操作方法)

方法名称 说明
Stream filter(Predicate<? super T> predicate) 用于对流中的数据进行过滤
Stream limit(long maxSize) 获取前几个元素
Stream skip(long n) 跳过前几个元
Stream distant() 去除流中重复的元素。依赖(hashCode和equals方法)
static Stream concat(Stream a,Stream b) 合并a,b两个流为一个流
public class StreamDemo3 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张无忌");
        list.add("小昭");
        list.add("周芷若");
        list.add("张三丰");
        list.add("张三丰");
        list.add("张强");

//        list.stream().filter(s-> s.startsWith("张")).forEach(s -> System.out.println(s));
        list.stream().filter(s -> s.startsWith("张")).forEach(System.out::println);
        System.out.println("-----------");

        long size = list.stream().filter(s -> s.length() == 3).count();
        System.out.println(size);

        list.stream().filter(s -> s.startsWith("张")).limit(2).forEach(System.out::println);
        System.out.println("-----------");
        list.stream().filter(s -> s.startsWith("张")).skip(2).forEach(System.out::println);
        System.out.println("-----------");

        //Map加工方法
        //给集合元素前面都加一个:java
        list.stream().map(s -> "java" + s).forEach(s -> System.out.println(s));
        System.out.println("-----------");

        //需求:把所有的名称  都加工成一个学生对象
        list.stream().map(s -> new Student(s)).forEach(student -> System.out.println(student));
//        list.stream().map(Student::new).forEach(System.out::println);//构造器引用    方法引用
        System.out.println("-----------");
        //合并流
        Stream<String> s1 = list.stream().filter(s -> s.startsWith("张"));
        Stream<String> s2 = Stream.of("java","Java");
        //public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
        Stream<String> s3 = Stream.concat(s1, s2);
        //s3.forEach(s -> System.out.println(s));
        s3.distinct().forEach(s -> System.out.println(s));//distinct去重复
    }
}

output:
张无忌
张三丰
张三丰
张强
-----------
4
张无忌
张三丰
-----------
张三丰
张强
-----------
java张无忌
java小昭
java周芷若
java张三丰
java张三丰
java张强
-----------
Student{name='张无忌'}
Student{name='小昭'}
Student{name='周芷若'}
Student{name='张三丰'}
Student{name='张三丰'}
Student{name='张强'}
-----------
张无忌
张三丰
张强
java
Java

注意:

  • 中间方法也称非终结方法,调用完成后返回新的Stream流可以继续使用,支持链式编程
  • 在Stream流中无法直接修改集合、数组中的数据。

Stream流中常见终结操作的方法

方法名称 说明
void forEach(Consumer action) 对此流的每个元素执行遍历操作
long count() 返回此流中元素数

注意:终结操作方法,调用完成流就无法继续使用,原因是不会再返回Stream了

Stream流的综合应用

案例:

需求:某个公司的开发部门,分为开发一部和二部,现在需要进行年中数据结算。

分析:

  1. 员工信息至少包含了(名称、性别、工资、奖金、处罚记录)
  2. 开发一部有4个员工、开发二部有5名员工
  3. 分别筛选出2个部门的最高工资的员工信息,封装成优秀员工对象Topperformer
  4. 分别统计出2个部门的平均月收入,要求去掉最高工资和最低工资
  5. 统计2个开发部整体的平均工资,去掉最低和最高工资的平均值

收集Stream流

public class SteamDemo04 {
    public static double allMoney;
    public static double allMoney2;

    public static void main(String[] args) {
        List<Employee> one = new ArrayList<>();
        one.add(new Employee("猪八戒", '男', 30000, 22000, null));
        one.add(new Employee("孙悟空", '男', 25000, 1000, "顶撞上司"));
        one.add(new Employee("沙僧", '男', 20000, 20000, null));
        one.add(new Employee("小白龙", '男', 20000, 25000, null));

        List<Employee> two = new ArrayList<>();
        two.add(new Employee("武松", '男', 15000, 9000, null));
        two.add(new Employee("李逵", '男', 20000, 10000, null));
        two.add(new Employee("西门庆", '男', 50000, 100000, "被打"));
        two.add(new Employee("潘金莲", '女', 3500, 9000, "被打"));
        two.add(new Employee("武大郎", '男', 20000, 9000, "下毒"));
        //1.开发一部的最高员工的工资
        //指定大小规则了
//        Employee e = one.stream().max((e1, e2) -> Double.compare(e1.getBonus() + e1.getSalary(), e2.getBonus() + e1.getSalary())).get();
//        System.out.println("开发一部的最高工资员工是:" + e);
//
//        Employee er = two.stream().max((e1, e2) -> Double.compare(e1.getBonus() + e1.getSalary(), e2.getBonus() + e2.getSalary())).get();
//        System.out.println("开发二部的最高工资员工是:" + er);

        //封装为Topformer对象
        Topperformer t = one.stream().max((e1, e2) -> Double.compare(e1.getBonus() + e1.getSalary(), e2.getBonus() + e2.getSalary())).map(e
                -> new Topperformer(e.getName(), e.getBonus() + e.getSalary())).get();
        System.out.println("开发一部的最高工资人员为:" + t);
        Topperformer t1 = two.stream().max((e1, e2) -> Double.compare(e1.getBonus() + e1.getSalary(), e2.getBonus() + e2.getSalary())).
                map(e -> new Topperformer(e.getName(), e.getBonus() + e.getSalary())).get();
        System.out.println("开发二部最高工资人员为:" + t1);

        //2.统计平均工资,去掉最高工资跟最低工资
        one.stream().sorted((e1, e2) -> Double.compare(e1.getBonus() + e1.getSalary(), e2.getBonus() + e2.getSalary()))
                .skip(1).limit(one.size() - 2).forEach(e -> {
            allMoney += (e.getBonus() + e.getSalary());
        });
        System.out.println("开发一部的平均工资为:" + allMoney / (one.size() - 2));

        //统计两个部门整体的平均工资,去掉最高工资跟最低工资
        //合并两个流
        Stream<Employee> s1 = one.stream();
        Stream<Employee> s2 = two.stream();
        Stream<Employee> s3 = Stream.concat(s1, s2);
        s3.sorted((e1, e2) ->Double.compare(e1.getBonus()+e1.getSalary(),e2.getBonus()+e2.getSalary()))
                .skip(1).limit(one.size()-2).forEach(employee -> {
                    allMoney2+=(employee.getBonus()+employee.getSalary());
        });
        System.out.println("开发部的总工资为:"+allMoney2/(one.size()-2+two.size()));
    }
}
output:
开发一部的最高工资人员为:Topperformer{name='猪八戒', money=52000.0}
开发二部最高工资人员为:Topperformer{name='西门庆', money=150000.0}
开发一部的平均工资为:42500.0
开发部的总工资为:7142.857142857143

Stream流的收集操作

  • 收集Stream流的含义:就是把Stream流操作后的结果数据转回到集合或者数组中去
  • Stream流:方便操作集合/数组的手段。
  • 集合/数组 才是开发中的目的

Stream流的收集方法

名称 说明
R collect(Collector collector) 开始收集Stream流,指定收集器

Collections 工具类提供了具体的收集方式

名称 说明
public static Collector toList() 把元素收集到List集合中
public static Collector toSet() 把元素收集到Set集合中
public static Collector toMap(Function keyMapper , Function valueMapper) 把元素收集到Map集合中
public class StreamDemo05 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张无忌");
        list.add("殷素素");
        list.add("小昭");
        list.add("赵敏");
        list.add("张三丰");
        list.add("张三");
        Stream<String> s1 = list.stream().filter(s -> s.startsWith("张"));
        List<String> zhangList = s1.collect(Collectors.toList());
        System.out.println(zhangList);

        Stream<String> s2 = list.stream().filter(s -> s.startsWith("张"));
        Set<String> zhangSet = s2.collect(Collectors.toSet());
        System.out.println(zhangSet);

        Stream<String> s3 = list.stream().filter(s -> s.startsWith("张"));
        String[] arr = s3.toArray(String[]::new);
        System.out.println(Arrays.toString(arr));
//        Object[] arrs = s3.toArray();
//        System.out.println("arrs数组的内容是:"+ Arrays.toString(arrs));
    }
}
output:
[张无忌, 张三丰, 张三]
[张三, 张三丰, 张无忌]
[张无忌, 张三丰, 张三]

posted @ 2022-01-16 22:10  tryAgainCs  阅读(60)  评论(0编辑  收藏  举报