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();
    
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容