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

Stream流体验

需求:

  • 创建一个集合,存储多个字符串元素
  • 把集合中所有以”张”开头的元素存储到一个新的集合
  • 把”张”开头的集合中的长度为3的元素存储到一个新的集合
  • 遍历上一步得到的集合中的元素输出。 ```java List names = new ArrayList<>();

@BeforeEach public void array() { Collections.addAll(names, “张三丰”, “张无忌”, “周芷若”, “赵敏”, “张强”); }

@Test public void old() { System.out.println(names);

  1. // 1、从集合中找出姓张的放到新集合
  2. List<String> zhangList = new ArrayList<>();
  3. for (String name : names) {
  4. if (name.startsWith("张")) {
  5. zhangList.add(name);
  6. }
  7. }
  8. System.out.println(zhangList);
  9. // 2、找名称长度是3的姓名
  10. List<String> zhangThreeList = new ArrayList<>();
  11. for (String name : zhangList) {
  12. if (name.length() == 3) {
  13. zhangThreeList.add(name);
  14. }
  15. }
  16. System.out.println(zhangThreeList);

}

// stream 流方式 @Test public void stream() { names.stream().filter(s -> s.startsWith(“张”)).filter(s -> s.length() == 3).forEach(System.out::println); }

<a name="Y4zOe"></a>
## Stream流的获取
Stream 流的三类方法

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

集合获取 Stream 流的方式:可以使用 Collection 接口中的默认方法 `stream()` 生成流

- `default Stream<E> stream()`:获取当前集合对象的 Stream 流
```java
/** --------------------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();

数组获取 Stream 流的方式:

  • public static <T> Stream<T> stream(T[] array):获取当前数组的 Stream 流
  • public static<T> Stream<T> of(T... values):获取当前数组/可变数据的 Stream 流

    /** ---------------------数组获取流------------------------------   */
    String[] names = {"赵敏","小昭","灭绝","周芷若"};
    Stream<String> nameStream = Arrays.stream(names);
    Stream<String> nameStream2 = Stream.of(names);
    

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

  • Stream<T> filter(Predicate<? super T> predicate):用于对流中的数据进行过滤。

  • Stream<T> limit(long maxSize):获取前几个元素
  • Stream<T> skip(long n):跳过前几个元素
  • Stream<T> distinct():去除流中重复的元素。依赖(hashCodeequals 方法)
  • static <T> Stream<T> concat(Stream a, Stream b):合并 a 和 b 两个流为一个流 ```java names.stream().filter(s -> s.startsWith(“张”)).filter(s -> s.length() == 3).forEach(System.out::println); long size = names.stream().filter(s -> s.length() == 3).count(); System.out.println(size);

names.stream().filter(s -> s.startsWith(“张”)).limit(2).forEach(System.out::println);

names.stream().filter(s -> s.startsWith(“张”)).skip(2).forEach(System.out::println);

// map 加工方法: 第一个参数原材料 -> 第二个参数是加工后的结果。 // 给集合元素的前面都加上一个:Halo : names.stream().map(s -> “Halo :” + s).forEach(System.out::println);

// 需求:把所有的名称 都加工成一个学生对象。 names.stream().map(Student::new).forEach(System.out::println); // 构造器引用 方法引用

// 合并流。 Stream s1 = names.stream().filter(s -> s.startsWith(“张”)); Stream s2 = Stream.of(“Java1”, “Java2”); Stream s3 = Stream.concat(s1 , s2); s3.distinct().forEach(System.out::println);

注意:

- 中间方法也称为非终结方法,调用完成后返回新的 Stream 流可以继续使用,支持链式编程。
- 在 Stream 流中无法直接修改集合、数组中的数据。
```java
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
class Employee {
    private String name;
    private char sex;
    private double salary;
    private double bonus;
    private String punish; // 处罚信息
}

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
class Topperformer {
    private String name;
    private double money; // 月薪
}

private static double allMoney;
private static double allMoney2;
@Test
public void test() {
    List<Employee> one = new ArrayList<>();
    one.add(new Employee("猪八戒", '男', 30000, 25000, 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, 1000, "被打"));
    two.add(new Employee("武大郎", '女', 20000, 0, "下毒"));

    // 获取工资最高的员工
    Employee employee = one.stream().max(Comparator.comparingDouble(e -> e.getSalary() + e.getBonus())).get();
    System.out.println(employee);

    // 获取工资最高的员工并封装成 Topperformer 对象
    Topperformer topperformer = one.stream().max(Comparator.comparingDouble(e -> e.getSalary() + e.getBonus()))
        .map(e -> new Topperformer(e.getName(), e.getSalary() + e.getBonus())).get();
    System.out.println(topperformer);


    // 统计 one 部门的平均月收入,去掉最高和最低
    one.stream().sorted(Comparator.comparingDouble(e -> e.getSalary() + e.getBonus())).skip(1).limit(one.size() - 2).forEach(e -> {
        allMoney += (e.getSalary() + e.getBonus());
    });
    System.out.println("one 部平均工资是" + allMoney / (one.size() - 2));

    // 统计两个部门整体的平均工资,去掉最高和最低
    Stream<Employee> s1 = one.stream();
    Stream<Employee> s2 = two.stream();
    Stream<Employee> s3 = Stream.concat(s1, s2);
    s3.sorted(Comparator.comparingDouble(e -> e.getSalary() + e.getBonus())).skip(1).limit(one.size() + two.size() - 2).forEach(e -> {
        allMoney2 += (e.getSalary() + e.getBonus());
    });
    BigDecimal a = BigDecimal.valueOf(allMoney2);
    BigDecimal b = BigDecimal.valueOf(one.size() + two.size() - 2);
    System.out.println("两个部门平均工资是" + a.divide(b, 2, RoundingMode.HALF_UP));
}

Stream流的常见终结操作方法

  • void forEach(Consumer action):对此流的每个元素执行遍历操作
  • long count():返回此流中的元素数

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

Stream 流的收集操作

收集 Stream 流的含义:就是把 Stream 流操作后的结果数据转回到集合或者数组中去。
Stream 流的收集方法:

  • R collect(Collector collector):开始收集Stream流,指定收集器

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

  • public static <T> Collector toList():把元素收集到 List 集合中
  • public static <T> Collector toSet():把元素收集到 Set 集合中
  • public static Collector toMap(Function keyMapper , Function valueMapper):把元素收集到 Map 集合中 ```java List list = new ArrayList<>(); list.add(“张无忌”); list.add(“周芷若”); list.add(“赵敏”); list.add(“张强”); list.add(“张三丰”); list.add(“张三丰”);

Stream s1 = list.stream().filter(s -> s.startsWith(“张”)); List zhangList = s1.collect(Collectors.toList()); // 可变集合 zhangList.add(“java1”); System.out.println(zhangList);

// JDK 16 得到不可变集合 // List list1 = s1.toList(); // list1.add(“java”); // 报错 // System.out.println(list1);

// 注意: “流只能使用一次” Stream s2 = list.stream().filter(s -> s.startsWith(“张”)); Set zhangSet = s2.collect(Collectors.toSet()); System.out.println(zhangSet);

Stream s3 = list.stream().filter(s -> s.startsWith(“张”)); // Object[] arrs = s3.toArray(); // 转成 String 数组 String[] arrs = s3.toArray(String[]::new); System.out.println(“Arrays数组内容:” + Arrays.toString(arrs)); ``` 注意:流只能使用一次,一次收集后就不能使用