自迩

Stream

流(Stream)

Stream是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。

“集合讲的是数据,流讲的是计算”!

 

注意:

1)Stream 不会自己存储元素

2)Stream 不会改变源对象。相反,Stream会返回一个持有结果的新Stream

3)Stream 操作是延迟执行的。这意味Stream会等到需要结果的时候才执行

 

一、Stream的三个操作步骤

1. 创建Stream

2. 中间操作

3. 终止操作(终端操作)

 

二、创建Stream

1. 通过Collection 系列集合提供的stream() 或 parallelStream()

  List<String> list = new ArrayList<>();

  Stream<String> stream = list.stream();

 

2. 通过Arrays 中的静态方法stream() 获取数组流

  Employee[] emps = new Employee[10];

  Stream<Employee> stream = Arrays.stream(emps);

 

3. 通过Stream 类中的静态方法of()

  Stream<String> stream = Stream.of("aa", "bb", "cc");

 

4. 创建无限流

  1)迭代

  Stream<Integer> stream = Stream.iterate(0, x -> x+2);

  stream.limit(10).forEach(System.out::println);

  2) 生成

  Stream.generate(() -> Math.random()).limit(5).forEach(System.out.println);

 

二、中间操作

多个中间操作可以连接形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”(延迟加载)。

  List<Employee> employees = Arrays.asList(new Employee()...);

 

(一) 筛选与切片:

1. fileter   ——  接受Lambda,从流中排除某些元素

内部迭代:迭代操作由Stream API完成

中间操作(不执行任何操作):

  Stream<Employee> stream = employees.stream().filter(e -> e.getAge > 35);

  Stream<Employee> stream = employees.stream().filter(e -> {

    System.out.println("执行中间操作"); // 中间操作在没有终止操作的情况下不会执行

    return e.getAge > 35;

  });

终止操作(一次性执行全部内容,即“惰性求值”):

  stream.forEach(System.out::println); 

 

外部迭代:

  Iterator<Employee> it = employees.iterator();

  while(it.hasNext()) {

    System.out.println(it.next());

  }

 

2. limit     ——  截断流,使其元素不超过给定数量

  employees.stream()

         .filter(e -> e.getSalary() > 5000)

         .limit(2)

         .forEach(System.out::println);

  employees.stream()

         .fileter(e -> {

           System.out.println("短路"); // || 满足了就不执行

           return e.getSalary() > 5000;  

         }).limit(2).forEach(System.out::println);

 

3. skip(n) ——  跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit(n) 互补

  employees.stream().filter(e -> e.getSalary() > 5000).skip(2).forEach(System.out::println);

 

4. distinct ——  筛选,通过流所生成的元素的hashCode() 和equals() 去除重复元素

  employees.stream().filter(e -> e.getSalary() > 5000).skip(2).distinct().forEach(System.out:println);

 

(二) 映射

List<String> list = Arrays.asList("aaa", "bbb",...);

1. map ——  接收Lambda, 将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。

  list.stream().map(str -> str.toUpperCase()).forEach(System.out::println);

  employees.stream().map(Employee::getName).forEach(System.out::println);

2. flatMap ——  接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流

  有如下方法:

  public static Stream<Character> filterCharacter(String str) {

    List<Character> list = new ArrayList<>();

    for(Character ch : str.toCharArray()) {

      list.add(ch);

    }

    return list.stream();

  }

  // TestStream : 方法filterCharacter对应的类名

  Stream<Stream<Character>> stream = list.stream().map(TestStream::filterCharacter);  

  stream.forEach(sm -> {

    sm.forEach(System.out::println);

  });

  改用flatMap:

  // 与add()、addAll()两个方法有异曲同工之妙

  Stream<Character> sm = list.stream().flatMap(TeatStream::filterCharacter);

  sm.forEach(System.out::println);

 

(三) 排序

1. sorted() ——  自然排序(Comparable)

  list.stream().sorted().forEach(System.out::println);

 

2. sorted(Comparator com) ——  定制排序(Comparator)

  employees.stream().sorted((e1, e2) -> {

    if(e1.getAge().equals(e2.getAge())) {

      return e1.getName().compareTo(e2.getName());

    } else {

      return e1.getAge().compareTo(e2.getAge());

    }

  }).forEach(System.out::println);

 

三、终止操作

(一) 查找与匹配

1. allMatch ——  检查是否匹配所有元素

  boolean b = employees.stream().allMatch(e -> e.getStatus().equals(Status.BUSY));

 

2. anyMatch ——  检查是否匹配至少一个元素

  boolean b =employees.stream().anyMatch(e -> e.getStatus().equals(Status.BUSY));

 

3. noneMatch ——  检查是否没有匹配所有元素

  boolean b =employees.stream().noneMatch(e -> e.getStatus().equals(Status.BUSY));

 

4. findFirst ——  返回第一个元素

  Optional<Employee> op = employees.stream().sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())).findFirst();

  op.orElse(other);  // 若op中为空,可有一个替代的对象other(解决空指针异常)  

  op.get();

 

5. findAny ——  返回当前流中的任意元素

  Optional<Employee> op = employees.stream().filter(e -> e.getStatus().equals(Status.FREE)).findAny();      // 获取串行流 单个线程找

  Optional<Employee> op = employees.parallelStream().filter(e -> e.getStatus().equals(Status.FREE)).findAny();  // 获取并行流 多个线程同时找

 

6. count ——  返回流中的元素总个数

  Long count = employees.stream().count();

 

7. max ——  返回流中的最大值

  Optional<Employee> op = employees.stream().max((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));

 

8. min ——  返回流中的最小值

  Optional<Double> op = employees.stream().map(Employee::getSalary).min(Double::compare);

 

 (二) 归约

reduce(T identity, BinaryOperator) / reduce(BinaryOperator) —— 可以将流中元素反复结合起来,得到一个值

 

// 0 起始值  后面是个function  累计值  0首先作为x , list中的第一个值作为y,将和作为x继续运算 直到结束

1. Integer sum = list.stream().reduce(0, (x, y) -> x + y);

 

// 获取所有员工的工资总和

// sum是Double 中的静态方法  上面1.的reduce有起始值0 不可能为空 所以直接用Integer;而员工工资没有起始值,是有可能为空,所有返回optional 避免空指针

2. Optional<Double> op = employees.stream().map(Employee::getSalary()).reduce(Double::sum);

注:map和reduce的连接通常称为map-reduce模式,因Google用它进行网络搜索而出名。

 

(三)收集

collect——将流转换为其他形式。接受一个Collector接口的实现,用于给stream中元素做汇总的方法

 

List<String> list = employees.stream().map(Employee::getName).collect(Collectors.toList());

Set<String> set = employees.stream().map(Employee::getName).collect(Collectors.toSet());

HashSet<String> hs = employees.stream().map(Employee::getName).collect(Collectors.toCollection(HashSet::new));

1. 总数

Long count = employees.stream().collect(Collectors.counting());

 

2. 平均值

Double avg = employees.stream().collect(Collectors.averagingDouble(Employee::getSalary));

 

3. 总和

Double sum = employees.stream().collect(Collectors.summingDouble(Employee::getSalary));

 

4. 最大值

Optional<Employee> op = employees.stream().collect(Collectors.maxBy(e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));

 

5. 最小值

Optional<Double> op = employees.stream().map(Employee::getSalary).collect(Collectors.minBy(Double::compare));

 

6. 分组

Map<Status, List<Employee>> map = employees.stream().collect(Collectors.groupingBy(Employee::getStatus));

 

7. 多级分组

// 先按状态分组,再按年龄分组

Map<Status, Map<String, List<Employee>>> map = employees.stream().collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy(e -> {

  if (((Employee) e).getAge() <= 35) {

    return "青年";

  } else if (((Employee) e).getAge() <= 50) {

    return "中年";

  } else {

    return "老年";

  }

})));

 

8. 分区(分片)

Map<boolean, List<Employee>> map =employees.stream().collect(Collectors.partitioningBy(e -> e.getSalary() > 8000));

 

9. 直接求和,求平均值,求最大值 非常方便

DoubleSummaryStatistics dss = employees.stream().collect(Collectors.summarizingDouble(Employee::getSalary));

System.out.println(dss.getSum());

System.out.println(dss.getAverage());

System.out.println(dss.getMax());

 

10. 连接字符串

// 连接名字

String str = employees.stream().map(Employee::getName).collect(Collectors.joining());

// ,表示分隔符;===表示开头连接符;+++表示结尾连接符

String str = employees.stream().map(Employee::getName).collect(Collectors.joining(",", “===", "+++"))

 

posted on 2021-01-17 20:46  自迩  阅读(540)  评论(0)    收藏  举报

导航