Java8流操作

一、流操作

首先,定义一个基础的对象,用于测试:

@Data
@AllArgsConstructor
static class Student {
    private String name;
    private Integer age;
    private String address;
    private Integer rank;
    private String sex;
    private Double money;
}

Student a = new Student("A", 9, "1qaz", 3, "m", 15.3);
Student b = new Student("B", 15, "1qaz", 2, "m", 23.7);
Student c = new Student("C", 12, "1qaz", 1, "w", 6.4);
List<Student> list = new ArrayList<>();
list.add(a);
list.add(b);
list.add(c);

1.1、map映射

// 按照姓名进行映射 map,key是学生姓名,value是学生对象
Map<String, Student> map1 = list.stream().collect(
    Collectors.toMap(
        Student::getName, 
        Function.identity(), 
        (k1, k2) -> k1
    )
);

// 按照姓名进行映射 map,map的 key是学生姓名, value是对象的 sex属性
Map<String, String> map2 = list.stream().collect(
    Collectors.toMap(
        Student::getName, 
        Student::getSex, 
        (k1, k2) -> k1
    )
);

// 指定收集的 map class类型
map2 = list.stream().collect(
    Collectors.toMap(
        Student::getName, 
        Student::getSex, 
        (k1, k2) -> k1, 
        LinkedHashMap::new
    )
);

// 按照性别进行分组,分组的 key是学生性别,value是对应性别的学生对象集合
Map<String, List<Student>> map3 = list.stream().collect(
    Collectors.groupingBy(Student::getSex)
);

// 按照性别进行分组,分组的 key是学生性别,value是对应性别的学生姓名集合
Map<String, List<String>> map4 = list.stream().collect(
    Collectors.groupingBy(
        Student::getSex, 
        Collectors.mapping(Student::getName, Collectors.toList())
    )
);

// 计算不同性别的学生,拥有的金额总数
Map<String, Double> map5 = list.stream().collect(
    Collectors.groupingBy(
        Student::getSex, 
        Collectors.summingDouble(Student::getMoney)
    )
);

// 按照学生年龄在 10岁以下,10岁以上进行分组
Map<Boolean, List<Student>> map6 = list.stream().collect(
    Collectors.partitioningBy(s -> s.getAge() >= 10)
);

// 多重分组:先按照学生的性别进行分组,再按照姓名进行分组
Map<String, Map<String, List<Student>>> map7
    = list.stream().collect(
          Collectors.groupingBy(
              Student::getSex, 
              Collectors.groupingBy(Student::getName)
          )
      );

1.2、合并计算

// 计算所有学生的金额总数
// list.stream().collect(Collectors.summingDouble(Student::getMoney))
double total = list.stream().mapToDouble(Student::getMoney).sum();

// 将所有学生的姓名用 , 连接起来
String names = list.stream().map(Student::getName).collect(Collectors.joining(", "));

1.3、list属性转换

// 获取所有学生的地址信息
List<String> addressList = list.stream().map(Student::getAddress)
    .distinct()
    .collect(Collectors.toList());

1.4、排序

// 按照学生的年龄进行升序排序
List<Student> list1 = list.stream()
    .sorted(Comparator.comparingInt(Student::getAge))
    .collect(Collectors.toList());

// 按照学生的金钱进行降序排序
List<Student> list2 = list.stream()
    .sorted(Comparator.comparingDouble(Student::getMoney)
    .reversed())
    .collect(Collectors.toList());

// 按照学生的年龄进行升序排序,再按照学生的金钱进行降序排序
List<Student> list3 = list.stream().sorted(
    Comparator.comparingInt(Student::getAge)
    .thenComparing(Student::getMoney, Comparator.reverseOrder()))
    .collect(Collectors.toList());

1.5、Stream 流操作类型

流的操作类型分为两种:

  • Intermediate:一个流可以后面跟随零个或多个intermediate操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),就是说,仅仅调用到这类方法,并没有真正开始流的遍历。
  • Terminal:一个流只能有一个terminal操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以,这必定是流的最后一个操作。Terminal操作的执行,才会真正开始流的遍历,并且会生成一个结果。
List<String> list4 = list.stream()
    .filter(stu -> {
        System.out.println("filter "+ stu.getName());
        return stu.age > 10;
    })
    .map(apple -> {
        System.out.println("mapping " + apple.getName());
        return apple.name;
    })
    .limit(2)
    .collect(Collectors.toList());

输出的结果:

filter A
filter B
mapping B
filter C
mapping C

从上面的结果可以看出,Intermediate流操作类型也是分为 2种类型的。

  1. Intermediate A流操作下紧接是 Intermediate B流操作,B的计算不依赖于 A的数据转换结果,可以将 A B一起执行
  2. Intermediate A流操作下紧接是 Intermediate B流操作,B的计算依赖于 A的数据转换结果,B的执行需要等待 A执行完毕。
1.5.1、流的构造与转换:
// 1. Individual values
Stream stream = Stream.of("a", "b", "c");

// 2. Arrays
String [] strArray = new String[] {"a", "b", "c"};
stream = Stream.of(strArray);
stream = Arrays.stream(strArray);

// 3. Collections
List<String> list = Arrays.asList(strArray);
stream = list.stream();
1.5.2、常规的数值型聚合运算
IntStream.of(new int[]{1, 2, 3}).forEach(System.out::println);
IntStream.range(1, 3).forEach(System.out::println);
IntStream.rangeClosed(1, 3).forEach(System.out::println);
1.5.3、流也可以转换为其它数据结构
// 1. Array
String[] strArray1 = stream.toArray(String[]::new);
// 2. Collection
List<String> list1 = stream.collect(Collectors.toList());
List<String> list2 = stream.collect(Collectors.toCollection(ArrayList::new));
Set set1 = stream.collect(Collectors.toSet());
Stack stack1 = stream.collect(Collectors.toCollection(Stack::new));
// 3. String
String str = stream.collect(Collectors.joining()).toString();

1.6、Stream类的 API

public interface Stream<T> extends BaseStream<T, Stream<T>> {
    
    /**
     * 过滤并返回 predicate为 true的 Stream里的元素;
     * eg: 
     *     List<Apple> appleList = 
     *         list.stream().filter(apple -> apple.num > 10).collect(Collectors.toList());
     * @return the new stream
     */
    Stream<T> filter(Predicate<? super T> predicate);
    
    /**
     * 根据 mapper处理 Stream流数据,map生成的是个 1:1映射,并将其返回值作为新的 Stream流对象
     * eg: 
     *     List<String> appleNameList = list.stream().map(Apple::getName).collect(Collectors.toList());
     * @return the new stream
     */
    <R> Stream<R> map(Function<? super T, ? extends R> mapper);
    
    /**
     * 返回 IntStream包装流,可以进行一些快捷的数值操作
     * eg:
     *    int sum = appleList.stream().mapToInt(Apple::getNum).sum();
     *    OptionalInt optionalInt = appleList.stream().mapToInt(Apple::getNum).max();
     *    // 获取最大值前,需要去校验下 OptionalInt的真值
     *    int max = optionalInt.isPresent() ? optionalInt.getAsInt() : -1;
     * @return the new stream
     */
    IntStream mapToInt(ToIntFunction<? super T> mapper);
    
    LongStream mapToLong(ToLongFunction<? super T> mapper);
    
    DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);
    
    /**
     * flatMap把 Stream中的层级结构 扁平化,就是将最底层元素抽出来放到一起
     * eg:
     *    Stream<List<Integer>> inputStream = Stream.of(Arrays.asList(1), Arrays.asList(2, 3),
     *        Arrays.asList(4, 5, 6));
     *    Stream<Integer> outputStream = inputStream.flatMap((childList) -> childList.stream());
     * @return the new stream
     */
    <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
    
    IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper);
    
    LongStream flatMapToLong(Function<? super T, ? extends LongStream> mapper);
    
    DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper);
    
    /**
     * 过滤 Stream里面重复的元素,并返回去重结果的 Stream流
     * eg:
     *    List<Integer> idList = appleList.stream().map(Apple::getId).distinct().collect(Collectors.toList());
     *
     * @return the new stream
     */
    Stream<T> distinct();
    
    /**
     * 返回流对象里面元素排序后的新的流对象, 按照流里面元素的默认排序规则
     * 若元素不支持排序,则会抛出 java.lang.ClassCastException异常
     * eg:
     *    appleList.stream().sorted()
     * @return the new stream
     */
    Stream<T> sorted();
    
    /**
     * 按照自定义的排序规则进行排序
     * eg: (Comparator工具类或者自定义 Comparator对象)
     *    appleList.stream().sorted(Comparator.comparing(Apple::hashCode));
     *    appleList.stream().sorted((a, b) -> a.num - b.num);
     * @return the new stream
     */
    Stream<T> sorted(Comparator<? super T> comparator);
    
    /**
     * 属于 intermediate操作,返回新的流。类似于 foreach操作,但是 foreach操作是 termediate操作
     * eg:
     *    appleList.stream().peek(System.out::println).map(Apple::getName)
     * @return the new stream
     */
    Stream<T> peek(Consumer<? super T> action);
    
    /**
     * 从流中返回前 maxSize的元素组成的新的流,如果 maxSize超过原流的元素长度,则抛出异常
     * eg:
     *    appleList.stream().limit(2).map(Apple::getName)
     * @return the new stream
     */
    Stream<T> limit(long maxSize);
    
    /**
     * 从流中去除前 n个元素,剩下的元素组成的新的流,如果 n超过原流的元素长度,则返回空的流对象
     * eg:
     *    // 取第 3-5个流元素
     *    appleList.stream().limit(5).skip(2).map(Apple::getName)
     * @return the new stream
     */
    Stream<T> skip(long n);
    
    void forEach(Consumer<? super T> action);
    
    /**
     * 与 foreach的主要区别在于并行流的处理上,forEachOrdered遍历流元素顺序与流元素的顺序严格一致
     * eg:
     *    appleList.stream().parallel().forEachOrdered(Apple::getName);
     */
    void forEachOrdered(Consumer<? super T> action);
    
    /**
     * 返回一个由流元素组成的 Object数组
     *
     * eg:
     *     Object[] arr = appleList.stream.map(Apple::getName).toArray();
     * @return the object array
     */
    Object[] toArray();
    
    /**
     * 返回一个由流元素组成的 指定类型数组
     *
     * eg:
     *     String[] arr = appleList.stream.map(Apple::getName).toArray(String[]::new);
     * @return A array
     */
    <A> A[] toArray(IntFunction<A[]> generator);
    
    /**
     * 提供一个起始值,按照运算规则 BinaryOperator和 Stream的每个元素依次进行组合,返回最终结果
     * 从这个意义上说,字符串拼接、数值的 sum、min、max都是特殊的reduce。
     * eg:
     *    // 求和
     *    appleList.stream().map(Apple::getNum).reduce(0, (sum, curr) -> sum + curr);
     *    appleList.stream().map(Apple::getNum).reduce(0, Integer::sum);
     *    appleList.stream().mapToInt(Apple::getNum).sum();
     *    // 字符串连接
     *    appleList.stream().map(Apple::getName).reduce("", (a, b) -> a + b);
     *    appleList.stream().map(Apple::getName).reduce("", String::concat);
     *    // 求最小值
     *    appleList.stream().map(Apple::getNum).reduce(Integer.MAX_VALUE, Integer::min); 
     *    // 求最大值
     *    appleList.stream().map(Apple::getNum).reduce(Integer.MIN_VALUE, Integer::max); 
     *
     * @return 组合结果
     */
    T reduce(T identity, BinaryOperator<T> accumulator);
    
    /**
     * 不提供起始值,按照运算规则 BinaryOperator和 Stream的每个元素依次进行组合,返回最终结果
     * eg:
     *    // 求和
     *    Optional<Integer> optional = appleList.stream().map(Apple::getNum).reduce(0, Integer::sum);
     *    int sumNum = optional.isPresent() ? optional.get() : 0;
     *
     * @return 组合结果
     */
    Optional<T> reduce(BinaryOperator<T> accumulator);
    
    <R, A> R collect(Collector<? super T, A, R> collector);
    
    Optional<T> min(Comparator<? super T> comparator);
    
    Optional<T> max(Comparator<? super T> comparator);
    
    /**
     * 返回流里面的元素个数
     *    appleList.stream().count() 就等于下面的语句
     *    appleList.stream().mapToLong(apple -> 1L).sum();
     * @return 元素个数
     */
    long count();
    
    /**
     * Stream 中全部元素符合传入的 predicate,返回 true
     * eg:
     *    appleList.stream().map(Apple::getNum).anyMatch(num -> num > 15);
     *
     * @return boolean
     */
    boolean anyMatch(Predicate<? super T> predicate);
    
    /**
     * Stream 中只要有一个元素符合传入的 predicate,返回 true
     * eg:
     *    appleList.stream().map(Apple::getNum).anyMatch(num -> num > 15);
     *
     * @return boolean
     */
    boolean allMatch(Predicate<? super T> predicate);
    
    /**
     * Stream 中没有一个元素符合传入的 predicate,返回 true
     * eg:
     *    appleList.stream().map(Apple::getNum).noneMatch(num -> num > 15);
     *
     * @return boolean
     */
    boolean noneMatch(Predicate<? super T> predicate);
    
    /**
     * 返回Stream的第一个元素或者空
     * eg:
     *    appleList.stream().findFirst().orElse(null);
     *
     * @return 第一个元素
     */
    Optional<T> findFirst();
    
    /**
     * 返回Stream的某已个元素,若流为空则返回空
     * eg:
     *    appleList.stream().findAny().orElse(null);
     *
     * @return 第一个元素
     */
    Optional<T> findAny();
    
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 原文地址: 深蓝至尊 一. 流式处理简介 在我接触到java8流式处理的时候,我的第一感觉是流式处理让集合操作变得...
    咻咻咻i阅读 1,175评论 0 0
  • 流是Java API的新成员,它允许你以声明性方式处理数据集合流操作有两个重要的特点:流水线——很多流操作本身会返...
    海拉鲁大刷子阅读 364评论 0 0
  • 什么是流式操作 Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。Str...
    青年心路阅读 282评论 0 0
  • 什么是流式操作 Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。Str...
    Java天天阅读 705评论 0 0
  • Java 8 引入了Stream(流式操作),你可以通过该操作实现对集合的并行处理和函数式操作。Collectio...
    年少懵懂丶流年梦阅读 1,344评论 0 1