6、Stream

1、介绍

目的:用于简化集合和数组操作的 API,结合了 Lambda 表达式
Stream 流思想的核心

  • 先得到集合或者数组的 Stream 流(就是一根传送带)
  • 把元素放上去
  • 然后就用这个 Stream 流简化的 API 来方便的操作元素
List<String> names = new ArrayList<>();
Collections.addAll(names, "张无忌", "周芷若", "赵敏", "张强", "张三丰");

names.stream()
    .filter(name -> name.startsWith("张"))
    .filter(name -> name.length() == 3)
    .forEach(System.out::println);

Stream 流的三类方法

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

2、获取 Stream 流

Stream 操作集合或者数组的第一步是先得到 Stream 流,然后才能使用流的功能
集合获取 Stream 流的方式:可以使用 Collection 接口中的默认方法 stream() 生成流

方法名 说明
default Stream<E> stream() 获取当前集合对象的 Stream 流
Map<String, Integer> map = new HashMap<>();

Set<String> keySet = map.keySet();
Stream<String> keyStream = keySet.stream();

Collection<Integer> values = map.values();
Stream<Integer> valuesStream = values.stream();

Set<Map.Entry<String, Integer>> entries = map.entrySet();
Stream<Map.Entry<String, Integer>> entryStream = entries.stream();

数组获取 Stream 流的方式

方法名 说明
public static <T> Stream<T> stream(T[] array) 获取当前数组的 Stream 流(Arrays 的 API)
public static <T> Stream<T> of(T... values) 获取当前数组 / 可变数据的 Stream 流(Stream 的 API)
String[] names = {"张三", "李四", "王五"};

Stream<String> stringStream = Arrays.stream(names);

Stream<String> nameStream = Stream.of(names);

3、中间方法

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

名称 说明
static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) 合并 a 和 b 两个流为一个流
Stream<T> filter(Predicate<? super T> predicate) 用于对流中的数据进行过滤
<R> Stream<R> map(Function<? super T, ? extends R> mapper) 对流中的每个元素进行加工(Function<元素类型, 加工后的类型>)
Stream<T> limit(long maxSize) 获取前几个元素
Stream<T> skip(long n) 跳过前几个元素
Stream<T> distinct() 去除流中重复的元素,依赖 hashCode 和 equals 方法
Stream<T> sorted(Comparator<? super T> comparator) 根据比较器规则对流中元素进行排序
names.stream().map(new Function<String, String>() {
    @Override
    public String apply(String name) {
        return "亲爱的".concat(name);
    }
}).forEach(System.out::println);

注意:中间方法会返回新的流,原来的流就不能用了,流只能使用一次

Stream<String> stringStream = names.stream();

stringStream.filter(name -> name.startsWith("张")); // 会返回新的流, 但是这里并没有接收新的流对象

stringStream.forEach(System.out::println);          // 原来的流已经不能用了, 这里会报错
// IllegalStateException: stream has already been operated upon or closed

一眼看穿 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())));
        /*
        控制台打印:------------
        组1:[煎蛋_1, 煎蛋_1, 煎蛋_1, 煎蛋_1, 煎蛋_1]
        组2:[煎蛋_2, 煎蛋_2, 煎蛋_2, 煎蛋_2, 煎蛋_2]
        */
    }

    /**
     * 把二箱鸡蛋分别加工成煎蛋,然后放到一起【10个煎蛋】,分给10个学生
     */
    @Test
    public void flatMap() {
        eggs.stream()
                .flatMap(x -> Arrays.stream(x).map(y -> y.replace("鸡", "煎")))
                .forEach(x -> System.out.println("学生" + student++ + ":" + x));
        /*
        控制台打印:------------
        学生1:煎蛋_1
        学生2:煎蛋_1
        学生3:煎蛋_1
        学生4:煎蛋_1
        学生5:煎蛋_1
        学生6:煎蛋_2
        学生7:煎蛋_2
        学生8:煎蛋_2
        学生9:煎蛋_2
        学生10:煎蛋_2
        */
    }
}

4、终结方法

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

名称 说明
void forEach(Consumer<? super T> action) 对此流的每个元素执行遍历操作
long count() 返回此流中的元素数
Optional<T> max(Comparator<? super T> comparator) 根据比较器规则获取流中元素的最大值
public T get() 获取元素(Optional 的 API)

5、收集 Stream 流

就是把 Stream 流操作后的结果数据恢复到集合或者数组中去
Stream 流:方便操作集合 / 数组的手段
集合 / 数组:才是开发中的目的

名称 说明
R collect(Collector collector) 开始收集 Stream 流,指定收集器(集合)
Object[] toArray() 开始收集 Stream 流,结果为 Object 数组
<A> A[] toArray(IntFunction<A[]> generator) 开始收集 Stream 流,结果为指定类型数组

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

方法名 说明
public static <T> Collector toList() 把元素收集到 List 集合中
public static <T> Collector toSet() 把元素收集到 Set 集合中
public static Collector toMap(Function keyMapper, Function valueMapper) 把元素收集到 Map 集合中
Object[] objects = names.stream().toArray(); // Object 数组

String[] strings = names.stream().toArray(new IntFunction<String[]>() {
    @Override
    public String[] apply(int length) {
        return new String[length];
    }
});
//String[] strings = names.stream().toArray(String[]::new); 指定类型数组

List<String> result = names.stream().filter(name -> name.length() == 3).collect(Collectors.toList()); // 把元素收集到 List 集合中
System.out.println(result);

// 把元素收集到 Map 集合中
List<UserInfo> userInfoList = userService.batchGetUserInfoByUserIds(userIdList);
Map<Long, UserInfo> userInfoMap = userInfoList.stream().collect(Collectors.toMap(UserInfo::getUserId, userInfo -> userInfo));

6、加工演示

// 需求: 把 names 列表转为 Student 数组
public class Student {
    private String name;
}

List<String> names = new ArrayList<>();
Collections.addAll(names, "张无忌", "周芷若", "赵敏", "张强", "张三丰");
// 方式一
names.stream().map(new Function<String, Student>() {
    @Override
    public Student apply(String name) {
        return new Student(name);
    }
}).forEach(student -> students.add(student));
// names.stream().map(name -> new Student(name)).forEach(student -> students.add(student));
// names.stream().map(Student::new).forEach(students::add);

// 方式二
List<Student> studentList = names.stream().map(Student::new).collect(Collectors.toList());
System.out.println(studentList);
posted @ 2023-06-12 14:55  lidongdongdong~  阅读(19)  评论(0编辑  收藏  举报