阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

输出集合

30次阅读
没有评论

共计 3326 个字符,预计需要花费 9 分钟才能阅读完成。

我们介绍了 Stream 的几个常见操作:map()filter()reduce()。这些操作对 Stream 来说可以分为两类,一类是转换操作,即把一个 Stream 转换为另一个 Stream,例如map()filter(),另一类是聚合操作,即对 Stream 的每个元素进行计算,得到一个确定的结果,例如reduce()

区分这两种操作是非常重要的,因为对于 Stream 来说,对其进行转换操作 并不会触发任何计算!我们可以做个实验:

import java.util.function.Supplier; 
import java.util.stream.Stream;

public class Main {public static void main(String[] args)     {Stream<Long> s1 = Stream.generate(new NatualSupplier());
        Stream<Long> s2 = s1.map(n -> n * n);
        Stream<Long> s3 = s2.map(n -> n - 1);
        System.out.println(s3); // java.util.stream.ReferencePipeline$3@49476842
    }
}

class NatualSupplier implements Supplier<Long> {long n = 0;
    public Long get() {
        n++;
        return n;
    }
}

因为 s1 是一个 Long 类型的序列,它的元素高达 922 亿亿个,但执行上述代码,既不会有任何内存增长,也不会有任何计算,因为转换操作只是保存了转换规则,无论我们对一个 Stream 转换多少次,都不会有任何实际计算发生。

而聚合操作则不一样,聚合操作会立刻促使 Stream 输出它的每一个元素,并依次纳入计算,以获得最终结果。所以,对一个 Stream 进行聚合操作,会触发一系列连锁反应:

Stream<Long> s1 = Stream.generate(new NatualSupplier());
Stream<Long> s2 = s1.map(n -> n * n);
Stream<Long> s3 = s2.map(n -> n - 1);
Stream<Long> s4 = s3.limit(10);
s4.reduce(0, (acc, n) -> acc + n);

我们对 s4 进行 reduce() 聚合计算,会不断请求 s4 输出它的每一个元素。因为 s4 的上游是 s3,它又会向s3 请求元素,导致 s3s2请求元素,s2s1 请求元素,最终,s1Supplier 实例中请求到真正的元素,并经过一系列转换,最终被 reduce() 聚合出结果。

可见,聚合操作是真正需要从 Stream 请求数据的,对一个 Stream 做聚合计算后,结果就不是一个Stream,而是一个其他的 Java 对象。

输出为 List

reduce()只是一种聚合操作,如果我们希望把 Stream 的元素保存到集合,例如 List,因为List 的元素是确定的 Java 对象,因此,把 Stream 变为 List 不是一个转换操作,而是一个聚合操作,它会强制 Stream 输出每个元素。

下面的代码演示了如何将一组 String 先过滤掉空字符串,然后把非空字符串保存到 List 中:

import java.util.*;
import java.util.stream.*;

public class Main {public static void main(String[] args) {Stream<String> stream = Stream.of("Apple", "", null, "Pear", "  ", "Orange");
        List<String> list = stream.filter(s -> s != null && !s.isBlank()).collect(Collectors.toList());
        System.out.println(list);
    }
}

Stream 的每个元素收集到 List 的方法是调用 collect() 并传入 Collectors.toList() 对象,它实际上是一个 Collector 实例,通过类似 reduce() 的操作,把每个元素添加到一个收集器中(实际上是ArrayList)。

类似的,collect(Collectors.toSet())可以把 Stream 的每个元素收集到 Set 中。

输出为数组

把 Stream 的元素输出为数组和输出为 List 类似,我们只需要调用 toArray() 方法,并传入数组的“构造方法”:

List<String> list = List.of("Apple", "Banana", "Orange");
String[] array = list.stream().toArray(String[]::new);

注意到传入的“构造方法”是 String[]::new,它的签名实际上是IntFunction<String[]> 定义的 String[] apply(int),即传入int 参数,获得 String[] 数组的返回值。

输出为 Map

如果我们要把 Stream 的元素收集到 Map 中,就稍微麻烦一点。因为对于每个元素,添加到 Map 时需要 key 和 value,因此,我们要指定两个映射函数,分别把元素映射为 key 和 value:

import java.util.*;
import java.util.stream.*;

public class Main {public static void main(String[] args) {Stream<String> stream = Stream.of("APPL:Apple", "MSFT:Microsoft");
        Map<String, String> map = stream
                .collect(Collectors.toMap(// 把元素 s 映射为 key:
                        s -> s.substring(0, s.indexOf(':')),
                        // 把元素 s 映射为 value:
                        s -> s.substring(s.indexOf(':') + 1)));
        System.out.println(map);
    }
}

分组输出

Stream还有一个强大的分组功能,可以按组输出。我们看下面的例子:

import java.util.*;
import java.util.stream.*;

public class Main {public static void main(String[] args) {List<String> list = List.of("Apple", "Banana", "Blackberry", "Coconut", "Avocado", "Cherry", "Apricots");
        Map<String, List<String>> groups = list.stream()
                .collect(Collectors.groupingBy(s -> s.substring(0, 1), Collectors.toList()));
        System.out.println(groups);
    }
}

分组输出使用 Collectors.groupingBy(),它需要提供两个函数:一个是分组的 key,这里使用s -> s.substring(0, 1),表示只要首字母相同的String 分到一组,第二个是分组的 value,这里直接使用Collectors.toList(),表示输出为List,上述代码运行结果如下:

{A=[Apple, Avocado, Apricots],
    B=[Banana, Blackberry],
    C=[Coconut, Cherry]
}

可见,结果一共有 3 组,按 "A""B""C" 分组,每一组都是一个List

假设有这样一个 Student 类,包含学生姓名、班级和成绩:

class Student {int gradeId; // 年级
    int classId; // 班级
    String name; // 名字
    int score; // 分数
}

如果我们有一个 Stream<Student>,利用分组输出,可以非常简单地按年级或班级把Student 归类。

小结

Stream可以输出为集合:

Stream通过 collect() 方法可以方便地输出为ListSetMap,还可以分组输出。

正文完
星哥说事-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2024-08-05发表,共计3326字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中