Fork me on GitHub
0%

Java 8 特性--Stream 流

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());
 wechat
扫描上面图中二维码关注微信公众号