java Stream

1. Stream概述

Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。

Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。

Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。

这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。

元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。

2. 什么是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)实现。

3. Stream操作分类

  • 无状态:指元素的处理不受之前元素的影响;
  • 有状态:指该操作只有拿到所有元素之后才能继续下去。
  • 非短路操作:指必须处理所有元素才能得到最终结果;
  • 短路操作:指遇到某些符合条件的元素就可以得到最终结果,如 A || B,只要A为true,则无需判断B的结果。

4. Stream常用操作

1. 排序

  • List
# 数字排序
List<Integer> list = Arrays.asList(3, 2, 6, 5, 7, 1);
//升序:1, 2, 3, 5, 6, 7
List<Integer> asc = list.stream().sorted().collect(Collectors.toList());
//降序:7, 6, 5, 3, 2, 1
List<Integer> desc = list.stream().sorted((x, y) -> y - x).collect(Collectors.toList());

# 字符串排序
List<String> list = Arrays.asList("a", "b", "c", "aa", "ab", "bb", "bc", "ac", "ca");
//升序:a, aa, ab, ac, b, bb, bc, c, ca
List<String> asc = list.stream().sorted().collect(Collectors.toList());
//降序:ca, c, bc, bb, b, ac, ab, aa, a
List<String> desc = list.stream().sorted(Collections.reverseOrder()).collect(Collectors.toList());
//反转集合:ca, ac, bc, bb, ab, aa, c, b, a
Collections.reverse(list);

# 对象排序
List<User> list = Arrays.asList(
        new User(1, "A", "2022/03/26 10:23:21"),
        new User(2, "B", "2022/03/26 10:35:40"),
        new User(3, "C", "2022/03/26 09:26:38"),
        new User(4, "D", "2022/03/26 11:26:26"),
        new User(5, "E", "2022/03/26 10:26:45"));

//根据user.createTime升序:[User{id=3, name='C', createTime=Sat Mar 26 09:26:38 CST 2022}, User{id=1, name='A', createTime=Sat Mar 26 10:23:21 CST 2022}, User{id=5, name='E', createTime=Sat Mar 26 10:26:45 CST 2022}, User{id=2, name='B', createTime=Sat Mar 26 10:35:40 CST 2022}, User{id=4, name='D', createTime=Sat Mar 26 11:26:26 CST 2022}]
List<User> asc = list.stream().sorted(Comparator.comparing(User::getCreateTime)).collect(Collectors.toList());

//根据user.createTime降序:[User{id=4, name='D', createTime=Sat Mar 26 11:26:26 CST 2022}, User{id=2, name='B', createTime=Sat Mar 26 10:35:40 CST 2022}, User{id=5, name='E', createTime=Sat Mar 26 10:26:45 CST 2022}, User{id=1, name='A', createTime=Sat Mar 26 10:23:21 CST 2022}, User{id=3, name='C', createTime=Sat Mar 26 09:26:38 CST 2022}]
List<User> desc = list.stream().sorted(Comparator.comparing(User::getCreateTime).reversed()).collect(Collectors.toList());

//NullPointerException
List<User> list = Arrays.asList(
        new User(1, "A", "2022/03/26 10:23:21"),
        new User(2, "B", null),
        new User(3, "C", "2022/03/26 09:26:38"),
        new User(4, "D", "2022/03/26 11:26:26"),
        new User(5, "E", "2022/03/26 10:26:45"));
//如果待排序的数据中有一些是空值null,则上述排序方法会出现NPE异常。解决方法是构造Comparator时使用nullsLast明确空值处理方式,代码如下:
Comparator.comparing(User::getCreateTime, Comparator.nullsLast(Date::compareTo))
  • Map
//HashMap是无序的,当我们希望有顺序地去存储key-value时,需要使用LinkedHashMap
Map<String, String> map = new HashMap();
map.put("a", "1");
map.put("b", "3");
map.put("d", "4");
map.put("c", "2");

//按照key升序排列:{a=1, b=3, c=2, d=4}
LinkedHashMap<String, String> asc1 = new LinkedHashMap<>();
LinkedHashMap<String, String> asc2 = new LinkedHashMap<>();
map.entrySet().stream().sorted(Comparator.comparing(e -> e.getKey())).forEach(e -> asc1.put(e.getKey(), e.getValue()));
map.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(f -> asc2.put(f.getKey(), f.getValue()));
// Comparator.comparing(e -> e.getKey()) 等价于 Map.Entry.comparingByKey()

//按照key降序排列:{d=4, c=2, b=3, a=1}
LinkedHashMap<String, String> desc1 = new LinkedHashMap<>();
LinkedHashMap<String, String> desc2 = new LinkedHashMap<>();
map.entrySet().stream().sorted(Collections.reverseOrder(Comparator.comparing(e -> e.getKey()))).forEach(f -> desc1.put(f.getKey(), f.getValue()));
map.entrySet().stream().sorted(Collections.reverseOrder(Map.Entry.comparingByKey())).forEach(f -> desc2.put(f.getKey(), f.getValue()));

//按照value升序排列:{a=1, c=2, b=3, d=4}
LinkedHashMap<String, String> asc3 = new LinkedHashMap<>();
LinkedHashMap<String, String> asc4 = new LinkedHashMap<>();
map.entrySet().stream().sorted(Comparator.comparing(e -> e.getValue())).forEach(e -> asc3.put(e.getKey(), e.getValue()));
map.entrySet().stream().sorted(Map.Entry.comparingByValue()).forEach(f -> asc4.put(f.getKey(), f.getValue()));

//按照value降序排列:{d=4, b=3, c=2, a=1}
LinkedHashMap<String, String> desc3 = new LinkedHashMap<>();
LinkedHashMap<String, String> desc4 = new LinkedHashMap<>();
map.entrySet().stream().sorted(Collections.reverseOrder(Comparator.comparing(e -> e.getValue()))).forEach(f -> desc3.put(f.getKey(), f.getValue()));
map.entrySet().stream().sorted(Collections.reverseOrder(Map.Entry.comparingByValue())).forEach(f -> desc4.put(f.getKey(), f.getValue()));

2. List转Map

List<User> list = Arrays.asList(
        new User(1, "A", "2022/03/26 10:23:21"),
        new User(2, "B", "2022/03/26 10:35:40"),
        new User(3, "C", "2022/03/26 09:26:38"));

//key和value都是对象的属性
Map<Long, String> map1 = list.stream().collect(Collectors.toMap(User::getId, User::getName));

//key是对象的属性,value是对象
Map<Long, User> map2 = list.stream().collect(Collectors.toMap(User::getId, User -> User));

//重复key处理:如果k1==k2,使用k2对应值覆盖k1
Collectors.toMap(User::getId, User -> User, (k1, k2) -> k2)

//根据user.sex将数组分组
Map<String, List<User>> map3 = list.stream().collect(Collectors.groupingBy(User::getSex));

等价写法
Collectors.toMap(User::getId, User -> User) == Collectors.toMap(User::getId, Function.identity())

3. Map转List

Map<String, Integer> map = new HashMap<>();
map.put("a",2);
map.put("c",1);
map.put("b",3);

//默认顺序
List<User> list = map.entrySet().stream().map(e -> new User(e.getValue(), e.getKey())).collect(Collectors.toList());
//按value排序
List<User> list2 = map.entrySet().stream().sorted(Map.Entry.comparingByValue()).map(e -> new User(e.getValue(), e.getKey())).collect(Collectors.toList());

4. 从List中获取某个属性

List<User> list = Arrays.asList(
        new User(1, "A", "2022/03/26 10:23:21"),
        new User(2, "B", "2022/03/26 10:35:40"),
        new User(3, "C", "2022/03/26 09:26:38"),
        new User(4, "C", "2022/03/26 11:16:38"));

//获取所有User.name
List<String> names = list.stream().map(e -> e.getName()).collect(Collectors.toList());
List<String> names2 = list.stream().map(User::getName).collect(Collectors.toList());

//拼接对象的几个属性
List<String> info = list.stream().map(e -> e.getId() + "-" + e.getName()).collect(Collectors.toList());

5. 筛选并根据属性去重

List<User> list = Arrays.asList(
        new User(1, "A", "2022/03/26 10:23:21"),
        new User(2, "B", "2022/03/26 10:35:40"),
        new User(3, null, "2022/03/26 09:26:38"),
        new User(4, "B", "2022/03/26 11:16:38"));

//去重
List<String> names = list.stream().map(e -> e.getName()).distinct().collect(Collectors.toList());

//过滤name不空的对象
List<User> users = list.stream().filter(e -> e.getName() != null).collect(Collectors.toList());

6. 计算:最大,最小,平均值,和

List<Integer> list = Arrays.asList(1,4,5,2,3);
int max = list.stream().mapToInt(Integer::intValue).max().getAsInt();
int max2 = list.stream().max(Integer::compareTo).get();
int min = list.stream().mapToInt(Integer::intValue).min().getAsInt();
long sum = list.stream().mapToInt(Integer::intValue).sum();
long count = list.stream().count();
double average = list.stream().mapToInt(Integer::intValue).average().getAsDouble();

//使用IntSummaryStatistics获取统计信息
IntSummaryStatistics statistics = list.stream().mapToInt(Integer::intValue).summaryStatistics();
max = statistics.getMax();
min = statistics.getMin();
sum = statistics.getSum();
count = statistics.getCount();
average = statistics.getAverage();

7. reduce

List<Integer> list = Arrays.asList(1,2,3,4,5);
int sum = list.stream().reduce((a, b) -> a + b).get();

int sum2 = list.stream().reduce(0, (a, b) -> a + b);

//返回结果的类型和流中数据类型不一致时
list = Arrays.asList(Integer.MAX_VALUE, Integer.MAX_VALUE);
long sum3 = list.stream().reduce(0L, (a, b) -> a + b, (a, b) -> 0L);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值