Java 8 API 添加了一个新的抽象称为流 Stream,可以让你以一种声明的方式处理数据,Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。Stream API 可以极大提高 Java 程序员的生产力,让程序员写出高效率、干净、简洁的代码。这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation) 得到前面处理的结果。
Stream 的定义
Stream(流)是一个来自数据源的元素队列并支持聚合操作,元素是特定类型的对象,形成一个队列。 Java 中的 Stream 并不会存储元素,而是按需计算。
- 数据源:流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
- 聚合操作:类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。
和以前的 Collection 操作不同, Stream 操作还有两个基础的特征:
- Pipelining: 中间操作都会返回流对象本身。这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行 (laziness) 和短路 (short-circuiting)。
- 内部迭代: 以前对集合遍历都是通过 Iterator 或者 For-Each 的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式 (Visitor) 实现。
在 Java 8 中,集合接口有两个方法生成流:
- stream() − 为集合创建串行流。
- parallelStream() − 为集合创建并行流。
过滤
1 2 3 4 5 6 7 8 9 10 11 12
| List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList()); System.out.println("筛选后的列表: " + filtered);
// 统计空字符串数量 long count = strings.stream().filter(String::isEmpty).count(); System.out.println("空字符串数量为: " + count); // 流并行处理 count = strings.parallelStream().filter(String::isEmpty).count();
// 将字符串不为空的筛选出来并且以逗号分隔合并起来 String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", ")); System.out.println("合并字符串: " + mergedString);
|
map 映射
1 2 3 4
| List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5); // 求各元素的平方 List<Integer> squaresList = numbers.stream().map(i -> i * i).distinct().collect(Collectors.toList()); System.out.println("Squares List: " + squaresList);
|
统计
1 2 3 4 5 6 7
| List<Integer> integers = Arrays.asList(1, 2, 13, 4, 15, 6, 17, 8, 19); IntSummaryStatistics stats = integers.stream().mapToInt((x) -> x).summaryStatistics(); System.out.println("列表中最大的数 : " + stats.getMax()); System.out.println("列表中最小的数 : " + stats.getMin()); System.out.println("所有数之和 : " + stats.getSum()); System.out.println("平均数 : " + stats.getAverage());
|
分组
1 2 3 4 5 6 7 8 9 10
| // 根据景区等级分组 List<Scenic> scenicList = new ArrayList<>(); scenicList.add(new Scenic(1L, "西湖", 5)); scenicList.add(new Scenic(2L, "千岛湖", 5)); scenicList.add(new Scenic(3L, "乌镇", 4)); scenicList.add(new Scenic(4L, "雷峰塔", 3)); scenicList.add(new Scenic(4L, "灵隐寺", 4)); Map<Integer, List<Scenic>> mapGroup = scenicList.stream().collect(Collectors.groupingBy(Scenic::getScenicLevel)); System.out.println(mapGroup);
|
转成 Map
1 2 3 4 5 6 7 8 9 10
| /** * toMap 如果集合对象有重复的key,会报错Duplicate key .... */ //Map<Long, Scenic> scenicMap = scenicList.stream().collect(Collectors.toMap(Scenic::getId, a -> a)); //System.out.println(scenicMap);
// 可以用 (k1,k2)->k1 来设置,如果有重复的 key,则保留 key1,舍弃 key2 Map<Long, Scenic> scenicMap = scenicList.stream().collect(Collectors.toMap(Scenic::getId, a -> a, (k1, k2) -> k1)); System.out.println(scenicMap);
|
reduce sum 求和
1 2 3 4 5 6 7 8 9 10 11 12
| List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5); // 没有起始值时返回为Optional类型 Optional<Integer> sumOptional = integers.stream().reduce(Integer::sum); System.out.println(sumOptional.get());
// 可以给一个起始种子值 Integer sumReduce = integers.stream().reduce(0, Integer::sum); System.out.println(sumReduce);
//直接用sum方法 Integer sum = integers.stream().mapToInt(i -> i).sum(); System.out.println(sum);
|
根据某个属性求平均数
1 2 3
| // 等级平均数 OptionalDouble average = scenicList.stream().mapToInt(Scenic :: getScenicLevel).average(); System.out.println(average.getAsDouble());
|