huhuszl

知行合一 
[ 随笔 -    文章 -    评论 -    阅读 - ]
圆龄:4个月
粉丝:10
关注:6
首页
订阅
联系
管理
阅读排行榜
......
评论排行榜
......
推荐排行榜
......
随笔分类
......
随笔档案
......
最新评论
......
最新随笔
......

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 @ 2023-02-21 22:21  huhuszl  阅读(81)  评论(0编辑  收藏  举报