huhuszl

知行合一 
[ 随笔 - 6    文章 - 0    评论 - 1    阅读 - 403 ]
圆龄:2年5个月
粉丝:0
关注:2
首页
订阅
联系
管理

Stream 流

简介

  • Stream 流是 Java 8 新提供给开发者的一组操作集合的 API,
  • 将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选、排序、聚合等。
  • 元素流在管道中经过中间操作(intermediate operation)的处理,最后由终端操作 (terminal operation) 得到前面处理的结果。
  • Stream 流可以极大的提高开发效率,也可以使用它写出更加简洁明了的代码。

创建 Stream 流

1. 用 list 创建流

  • 示例
    List<String> list = Arrays.asList("a", "b", "c");
    // 创建一个顺序流
    Stream<String> stream = list.stream();
    // 创建一个并行流
    Stream<String> parallelStream = list.parallelStream();
    

2. 用数组创建流

  • 示例
    int[] array={1,3,5,7,9};
    IntStream stream = Arrays.stream(array);
    

3. 自定义Stream流

  • of方法
    Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
    
  • iterate方法
    Stream<Integer> stream2 = Stream.iterate(0, (x) -> x + 3).limit(4);
    
  • generate方法
    Stream<Double> stream3 = Stream.generate(Math::random).limit(3);
    

API

1. 匹配与遍历

  • 示例
    List<Integer> list = Arrays.asList(7, 6, 9, 3, 8, 2, 1);
    
    // 遍历输出符合条件的元素
    List<Integer> collect = list.stream().filter(x -> x > 6).collect(Collectors.toList());
    // 匹配第一个
    Optional<Integer> findFirst = list.stream().filter(x -> x > 6).findFirst();
    // 匹配任意(适用于并行流)
    Optional<Integer> findAny = list.parallelStream().filter(x -> x > 6).findAny();
    // 是否包含符合特定条件的元素
    boolean anyMatch = list.stream().anyMatch(x -> x > 6);
    
    System.out.println("大于6的值:" + collect);
    System.out.println("匹配第一个值:" + findFirst.get());
    System.out.println("匹配任意一个值:" + findAny.get());
    System.out.println("是否存在大于6的值:" + anyMatch);
    

2. 筛选(filter)

  • 筛选,是按照一定的规则校验流中的元素,将符合条件的元素提取到新的流中的操作。
    public static void main(String[] args) {
    	List<Person> personList = new ArrayList<>();
    	personList.add(new Person("张三", 1000, 20, "男", "北京"));
    	personList.add(new Person("李四", 2000, 21, "男", "南京"));
    	personList.add(new Person("王五", 3000, 20, "女", "合肥"));
    	personList.add(new Person("赵六", 4000, 22, "男", "四川"));
    	personList.add(new Person("孙七", 5000, 25, "女", "上海"));
    	// 筛选出工作高于3000的员工
    	List<String> list = personList.stream()
    						.filter(p -> p.getSalary() > 3000)
    						.map(Person::getName)
    						.collect(Collectors.toList());
    	System.out.println("薪资高于3000元的员工:" + list);
    }
    

3. 聚合(min/max/count/sum)

  • max、min、count 这些大家都不陌生,在mysql中我们常用它们进行数据运算和统计。
  • Java stream中也引入了这些概念和用法,极大地方便了我们对集合、数组的数据统计工作。
    public static void main(String[] args) {
    	List<Person> personList = new ArrayList<>();
    	personList.add(new Person("张三", 1000, 20, "男", "北京"));
    	personList.add(new Person("李四", 2000, 21, "男", "南京"));
    	personList.add(new Person("王五", 3000, 20, "女", "合肥"));
    	personList.add(new Person("赵六", 4000, 22, "男", "四川"));
    	personList.add(new Person("孙七", 5000, 25, "女", "上海"));
    
    	// 获取工资最高的员工
    	Optional<Person> min = personList.stream().min(Comparator.comparingInt(Person::getSalary));
    	System.out.println("员工工资最大值:" + min.get().getSalary());
    
    	// 获取工资最高的员工
    	Optional<Person> max = personList.stream().max(Comparator.comparingInt(Person::getSalary));
    	System.out.println("员工工资最大值:" + max.get().getSalary());
    
    	// 计算工资大于2000的有多少人
    	long count = personList.stream().filter(p -> p.getSalary() > 2000).count();
    	System.out.println("工资大于2000元的人数:" + count);
    
    	// 计算所有员工工资总和
    	int sum = personList.stream().mapToInt(Person::getSalary).sum();
    	System.out.println("所有员工工资总和:" + sum);
    }
    

4.映射(peek/map/flatMap)

  • 映射,可以将一个流的元素按照一定的映射规则映射到另一个流中。分为 map 和 flatMap :
    • peek:接收一个 Consumer 作为参数,为每个元素提供消费函数。
      • action 字面理解,就知道 其代表一个动作。Consumer 代表无返回值的函数,只执行函数。
    • map:接收一个 Function 作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
      • Function代表有返回值的函数。
    • flatMap:接收一个 Function 作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
      • 指定类型的函数,方法都要继承 Stream 类
      Stream peek(Consumer<? super T> action)
      
      Stream map(Function<? super T,? extends R> mapper)
      
      Stream flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)
      
  • 作用
    • peek方法
      • 一般用于调试,示例
        public static void main(String[] args) {
        	List<Person> personList = new ArrayList<>();
        	personList.add(new Person("张三", 1000, 20, "男", "北京"));
        	personList.add(new Person("李四", 2000, 21, "男", "南京"));
        	personList.add(new Person("王五", 3000, 20, "女", "合肥"));
        	personList.add(new Person("赵六", 4000, 22, "男", "四川"));
        	personList.add(new Person("孙七", 5000, 25, "女", "上海"));
        
        	List<Person> personListNew = personList.stream()
        		.peek(System.out::println)
        		.peek(person -> person.setSalary(person.getSalary() + 10000))
        		.peek(System.out::println)
        		.collect(Collectors.toList());
        }
        
      • 控制台 输出
        /*
        	Person{name='张三', salary=1000, age=20, sex='男', area='北京'}
        	Person{name='张三', salary=11000, age=20, sex='男', area='北京'}
        
        	Person{name='李四', salary=2000, age=21, sex='男', area='南京'}
        	Person{name='李四', salary=12000, age=21, sex='男', area='南京'}
        
        	Person{name='王五', salary=3000, age=20, sex='女', area='合肥'}
        	Person{name='王五', salary=13000, age=20, sex='女', area='合肥'}
        
        	Person{name='赵六', salary=4000, age=22, sex='男', area='四川'}
        	Person{name='赵六', salary=14000, age=22, sex='男', area='四川'}
        
        	Person{name='孙七', salary=5000, age=25, sex='女', area='上海'}
        	Person{name='孙七', salary=15000, age=25, sex='女', area='上海'}
        */
        
    • map方法
      • for循环 示例
        List<Integer> numList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);
        List<String> strList = new ArrayList<>();
        for (int num : numList) {
        	strList.add(Integer.toString(num));
        }
        
      • Stream流 示例
        List<String> strList = numList.stream()
        	.map(it -> Integer.toString(it))
        	.collect(Collectors.toList());
        
    • flatMap方法
      • for循环 示例
        List<Klass> result = new ArrayList<>();
        for (KlassGroup group : groupList) {
        	for (Klass klass : group.getKlassList()) {
        		result.add(klass);
        	}
        }
        
      • Stream流 示例
        List<Klass> result = groupList.stream()
        		.flatMap(it -> it.getKlassList().stream())
        		.collect(Collectors.toList());
        
    • map方法与flatMap方法的区别
      • 示例
        public class Map_FlatMap {
        
        	List<String[]> eggs = new ArrayList<>();
        
        	@Before
        	public void init() {
        		// 第一箱鸡蛋
        		eggs.add(new String[]{"鸡蛋_1", "鸡蛋_1", "鸡蛋_1", "鸡蛋_1", "鸡蛋_1"});
        		// 第二箱鸡蛋
        		eggs.add(new String[]{"鸡蛋_2", "鸡蛋_2", "鸡蛋_2", "鸡蛋_2", "鸡蛋_2"});
        	}
        
        	// 自增生成组编号
        	static int group = 1;
        	// 自增生成学生编号
        	static int student = 1;
        
        	/**
        	 * 把二箱鸡蛋分别加工成煎蛋,还是放在原来的两箱,分给2组学生
        	 */
        	@Test
        	public void map() {
        		eggs.stream()
        				.map(x -> Arrays.stream(x).map(y -> y.replace("鸡", "煎")))
        				.forEach(x -> System.out.println("组" + group++ + ":" + Arrays.toString(x.toArray())));
        	}
        
        	/**
        	 * 把二箱鸡蛋分别加工成煎蛋,然后放到一起【10个煎蛋】,分给10个学生
        	 */
        	@Test
        	public void flatMap() {
        		eggs.stream()
        				.flatMap(x -> Arrays.stream(x).map(y -> y.replace("鸡", "煎")))
        				.forEach(x -> System.out.println("学生" + student++ + ":" + x));
        	}
        
        }
        
      • map方法 控制台
        /*
        	控制台打印:------------
        	组1:[煎蛋_1, 煎蛋_1, 煎蛋_1, 煎蛋_1, 煎蛋_1]
        	组2:[煎蛋_2, 煎蛋_2, 煎蛋_2, 煎蛋_2, 煎蛋_2]
        */
        
      • flatMap方法 控制台
        /*
        	控制台打印:------------
        	学生1:煎蛋_1
        	学生2:煎蛋_1
        	学生3:煎蛋_1
        	学生4:煎蛋_1
        	学生5:煎蛋_1
        	学生6:煎蛋_2
        	学生7:煎蛋_2
        	学生8:煎蛋_2
        	学生9:煎蛋_2
        	学生10:煎蛋_2
        */
        
    • map方法peek方法的区别
      List<Person> personListNew = personList.stream()
      	 .peek(person -> person.setSalary(person.getSalary() + 10000))
      	 .peek(System.out::println)
      	 .collect(Collectors.toList());
      
      List<Person> personListNew = personList.stream()
      	.map(person -> { person.setSalary(person.getSalary() + 10000); return person;})
      	.peek(System.out::println)
      	.collect(Collectors.toList());
      

5. 归约(reduce)

  • 归约,也称缩减,顾名思义,是把一个流缩减成一个值,能实现对集合 求和、求乘积 等操作。
    • 工资之和
      Optional<Integer> sumSalary = personList.stream().map(Person::getSalary).reduce(Integer::sum);
      
      Integer sumSalary = personList.stream().reduce(0, (sum, p) -> sum += p.getSalary(), Integer::sum);
      
    • 求乘积
      OptionalLong sumSalary = personList.stream().mapToLong(Person::getSalary).reduce((sum, a)-> sum*=a);
      
      long sumSalary = personList.stream().mapToLong(Person::getSalary).reduce(1,(sum, a)-> sum*=a);
      
  • reduce 参数
    • 解释

      • 三参数 (U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);
        初始值,默认执行方法,并发流的执行方法 - (目的为了满足多线程的计算)。
      • 二参数 (U identity, BinaryOperator op)
        初始值,默认执行方法。
      • 一参数 (BinaryOperator<T> accumulator)
        默认执行方法。
    • 三个参数

      Integer sumSalary = personList.stream().reduce(0, (sum, a) -> sum += a.getSalary(), Integer::sum);
      
    • 两个参数

      Integer sumSalary = personList.stream().mapToInt(Person::getSalary).reduce(1,(sum, a)-> sum+=a);
      
    • 一个参数

      Optional<Integer> sumSalary2 = personList.stream().map(Person::getSalary).reduce(Integer::sum);
      

6. 收集(collect)

  • 解释
    • collect,收集,可以说是内容最繁多、功能最丰富的部分了。从字面上去理解,就是把一个流收集起来,最终可以是收集成一个值也可以收集成一个新的集合。
    • collect 主要依赖 java.util.stream.Collectors 类内置的静态方法。
  • 6.1 归集(toList/toSet/toMap)
    • 因为流不存储数据,那么在流中的数据完成处理后,需要将流中的数据重新归集到新的集合里。
    • toList、toSet和toMap比较常用,另外还有toCollection、toConcurrentMap等复杂一些的用法。
    • toList()
      List<Integer> list = Arrays.asList(1, 3, 4, 8, 6, 2, 20, 13);
      List<Integer> list1 = list.stream().filter(a -> a % 2 == 0).collect(Collectors.toList());
      
    • toSet()
      List<Integer> list = Arrays.asList(1, 3, 4, 8, 6, 2, 20, 13);
      Set<Integer> list2 = list.stream().filter(a -> a % 2 == 0).collect(Collectors.toSet());
      
    • toMap()
      List<Person> personList = new ArrayList<>();
      personList.add(new Person("张三", 1000, 20, "男", "北京"));
      personList.add(new Person("李四", 2000, 21, "男", "南京"));
      personList.add(new Person("王五", 3000, 20, "女", "合肥"));
      personList.add(new Person("赵六", 4000, 22, "男", "四川"));
      personList.add(new Person("孙七", 5000, 25, "女", "上海"));
      // 工资大于3000元的员工
      Map<String, Integer> map = personList.stream()
      	.filter(person -> person.getSalary() > 3000)
      	.collect(Collectors.toMap(Person::getName, person -> person.getSalary()));
      
  • 6.2 统计(count/averaging)
    • 计数:count
    • 平均值:averagingInt、averagingLong、averagingDouble
    • 最值:maxBy、minBy
    • 求和:summingInt、summingLong、summingDouble
    • 统计以上所有:summarizingInt、summarizingLong、summarizingDouble
      List<Person> personList = new ArrayList<>();
      personList.add(new Person("张三", 1000, 20, "男", "北京"));
      personList.add(new Person("李四", 2000, 21, "男", "南京"));
      personList.add(new Person("王五", 3000, 20, "女", "合肥"));
      personList.add(new Person("赵六", 4000, 22, "男", "四川"));
      personList.add(new Person("孙七", 5000, 25, "女", "上海"));
      
      // 统计员工人数、平均工资、工资总额、最高工资
      // 员工总人数
      long count = personList.stream().count();
      // 平均工资
      Double average = personList.stream().collect(Collectors.averagingDouble(Person::getSalary));
      // 最高工资
      Optional<Integer> max = personList.stream().map(Person::getSalary).max(Integer::compare);
      // 工资之和
      int sum = personList.stream().mapToInt(Person::getSalary).sum();
      // 一次性统计所有信息
      DoubleSummaryStatistics collect = personList.stream().collect(Collectors.summarizingDouble(Person::getSalary));
      
      System.out.println("员工总人数:" + count);
      System.out.println("员工平均工资:" + average);
      System.out.println("员工工资总和:" + sum);
      System.out.println("员工工资所有统计:" + collect);
      
  • 6.3 分组(partitioningBy/groupingBy)
    • 分区:将stream按条件分为两个Map,比如员工按薪资是否高于8000分为两部分。
    • 分组:将集合分为多个Map,比如员工按性别分组。有单级分组和多级分组。
      List<Person> personList = new ArrayList<>();
      personList.add(new Person("张三", 1000, 20, "男", "北京"));
      personList.add(new Person("李四", 2000, 21, "男", "南京"));
      personList.add(new Person("王五", 3000, 20, "女", "合肥"));
      personList.add(new Person("赵六", 4000, 22, "男", "合肥"));
      personList.add(new Person("孙七", 5000, 25, "女", "上海"));
      
      // 按薪资高于3000分组
      Map<Boolean, List<Person>> salaryGroup = personList.stream().collect(Collectors.partitioningBy(p -> p.getSalary() > 3000));
      List<Person> group1 = salaryGroup.get(true);
      List<Person> group2 = salaryGroup.get(false);
      for (Person person : group1) {
      	System.out.println("薪资高于3000元组:" + person);
      }
      for (Person person : group2) {
      	System.out.println("薪资低于3000元组:" + person);
      }
      
      // 按性别分组
      Map<String, List<Person>> sexGroup = personList.stream().collect(Collectors.groupingBy(Person::getSex));
      List<Person> group3 = sexGroup.get("男");
      List<Person> group4 = sexGroup.get("女");
      for (Person person : group3) {
      	System.out.println("男子组:" + person);
      }
      for (Person person : group4) {
      	System.out.println("女子组:" + person);
      }
      
      // 将员工先按性别分组,再按地区分组
      Map<String, Map<String, List<Person>>> group = personList.stream().collect(Collectors.groupingBy(Person::getSex, Collectors.groupingBy(Person::getArea)));
      Map<String, List<Person>> manGroup = group.get("男");
      Map<String, List<Person>> womenGroup = group.get("女");
      List<Person> group5 = manGroup.get("合肥");
      List<Person> group6 = womenGroup.get("上海");
      System.out.println("地区在合肥的男子组:" + group5);
      System.out.println("地区在上海的女子组:" + group6);
      
  • 6.4 接合(joining)
    • joining可以将stream中的元素用特定的连接符(没有的话,则直接连接)连接成一个字符串。
      List<Person> personList = new ArrayList<>();
      personList.add(new Person("张三", 1000, 20, "男", "北京"));
      personList.add(new Person("李四", 2000, 21, "男", "南京"));
      personList.add(new Person("王五", 3000, 20, "女", "合肥"));
      personList.add(new Person("赵六", 4000, 22, "男", "合肥"));
      personList.add(new Person("孙七", 5000, 25, "女", "上海"));
      String persons = personList.stream().map(p -> p.getName() + "-" + p.getSex() + "-" + p.getSalary()).collect(Collectors.joining(","));
      System.out.println("所有员工信息:" + persons);
      
    • 控制台
      	/*
      		控制台打印:------------
      		所有员工信息:张三-男-1000,李四-男-2000,王五-女-3000,赵六-男-4000,孙七-女-5000
      	*/
      
  • 6.5归约(reducing)
    • Collectors类提供的reducing方法,相比于stream本身的reduce方法,增加了对自定义归约的支持。
    • reducing(不推荐使用)
      List<Integer> testData = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
      Integer sum = testData.stream().collect(Collectors.reducing(0, (prev, cur) -> {
          System.out.println("prev=>" + prev + "cur=>" + cur);
          return prev + cur;
      }));
      
    • reduce (推荐使用)
      List<Integer> testData = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
      Integer sum = testData.stream().reduce(0, (prev, cur) -> {
          System.out.println("prev=>" + prev + "cur=>" + cur);
          return prev + cur;
      });
      
  • 7. 排序(sorted)
    • sorted,中间操作。有两种排序:

    • sorted():自然排序,流中元素需实现Comparable接口

    • sorted(Comparator com):Comparator排序器自定义排序

      List<Person> personList = new ArrayList<>();
      personList.add(new Person("张三", 16000, 20, "男", "北京"));
      personList.add(new Person("李四", 8500, 21, "男", "南京"));
      personList.add(new Person("王五", 7300, 20, "女", "合肥"));
      personList.add(new Person("赵六", 8000, 22, "男", "合肥"));
      personList.add(new Person("孙七", 15860, 25, "女", "上海"));
      
      // 按工资升序排序(自然排序)
      List<String> newList = personList.stream().sorted(Comparator.comparing(Person::getSalary)).map(Person::getName).collect(Collectors.toList());
      // 按工资倒序排序
      List<String> newList2 = personList.stream().sorted(Comparator.comparing(Person::getSalary).reversed()).map(Person::getName).collect(Collectors.toList());
      // 先按工资再按年龄升序排序
      List<String> newList3 = personList.stream().sorted(Comparator.comparing(Person::getSalary).thenComparing(Person::getAge)).map(Person::getName).collect(Collectors.toList());
      // 先按工资再按年龄自定义排序(降序)
      List<String> newList4 = personList.stream().sorted((p1, p2) -> {
      	if (p1.getSalary().equals(p2.getSalary())) {
      		return p2.getAge() - p1.getAge();
      	} else {
      		return p2.getSalary() - p1.getSalary();
      	}
      }).map(Person::getName).collect(Collectors.toList());
      
      System.out.println("按工资升序排序:" + newList);
      System.out.println("按工资降序排序:" + newList2);
      System.out.println("先按工资再按年龄升序排序:" + newList3);
      System.out.println("先按工资再按年龄自定义降序排序:" + newList4);
      
  • 8.提取/组合
    • 流也可以进行合并、去重、限制、跳过等操作。
      String[] arr1 = {"a", "b", "c", "d"};
      String[] arr2 = {"d", "e", "f", "g"};
      
      Stream<String> stream1 = Stream.of(arr1);
      Stream<String> stream2 = Stream.of(arr2);
      // concat:合并两个流 distinct:去重
      List<String> newList = Stream.concat(stream1, stream2).distinct().collect(Collectors.toList());
      // limit:限制从流中获得前n个数据
      List<Integer> collect = Stream.iterate(1, x -> x + 2).limit(10).collect(Collectors.toList());
      // skip:跳过前n个数据
      List<Integer> collect2 = Stream.iterate(1, x -> x + 2).skip(1).limit(5).collect(Collectors.toList());
      
      System.out.println("流合并:" + newList);
      System.out.println("limit:" + collect);
      System.out.println("skip:" + collect2);
      
posted @   huhuszl  阅读(85)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
阅读排行榜
1. 谷歌登录 (服务端)(153)
2. 数据类型(111)
3. Stream 流(85)
4. 集合(27)
5. 面向对象(16)
评论排行榜
......
推荐排行榜
......
随笔分类
JAVA(5)
开发问题(1)
随笔档案
2024年9月(1)
2023年2月(5)
最新评论
......
最新随笔
......
点击右上角即可分享
微信分享提示