image.png

一、开发场景

书写查询相关业务代码时,需要调用微服务提供方返回的集合数据并对其进行筛选。比如筛选某字段与预期值匹配的所有数据。

  1. // 服务提供方返回的集合数据
  2. List<CentManlReimRegAndSetlDTO> list = pageResult.getData();
  3. // 参与筛选的预期值
  4. String medType = "xxx";

二、常规写法

  1. /*
  2. 利用foreach循环数据并判断筛选,填入新的集合中
  3. */
  4. List<CentManlReimRegAndSetlDTO> newList = new ArrayList<>();
  5. for (CentManlReimRegAndSetlDTO dto : list) {
  6. if(medType.equals(dto.getMedType())){
  7. newList.add(dto);
  8. }
  9. }
  10. return newList;

三、流操作写法

  1. /*
  2. 将集合转换为流,筛选后再转集合
  3. */
  4. List<CentManlReimRegAndSetlDTO> newList = list.stream()
  5. .filter(dto -> medType.equals(dto.getMedType()))
  6. .collect(Collectors.toList());
  7. return newList;

filter方法的参数Predicate是一个函数式接口,所以可以传递Lambda表达式,对数据进行过滤。

四、总结

stream 相对于 Collection 的优点

  • 无存储: 流并不存储值;流的元素源自数据源(可能是某个数据结构、生成函数或I/O通道等等),通过一系列计算步骤得到;
  • 函数式风格: 对流的操作会产生一个结果,但流的数据源不会被修改;
  • 惰性求值: 多数流操作(包括过滤、映射、排序以及去重)都可以以惰性方式实现。这使得我们可以用一遍遍历完成整个流水线操作,并可以用短路操作提供更高效的实现;
  • 无需上界: 不少问题都可以被表达为无限流(infinite stream):用户不停地读取流直到满意的结果出现为止(比如说,枚举 完美数 这个操作可以被表达为在所有整数上进行过滤);集合是有限的,但流可以表达为无线流;
  • 代码简练: 对于一些collection的迭代处理操作,使用 stream 编写可以十分简洁,如果使用传统的 collection 迭代操作,代码可能十分啰嗦,可读性也会比较糟糕;

    stream 和 iterator 迭代的效率比较

  • 传统 iterator (for-loop) 比 stream(JDK8) 迭代性能要高,尤其在小数据量的情况下;

  • 在多核情景下,对于大数据量的处理,parallel stream 可以有比 iterator 更高的迭代处理效率;

即:
迭代方式:可读性差,数据量小时效率高
流操作方式:可读性好,数据量大、并行处理时效率高


参考资料: JDK 8 Stream 数据流效率 jdk8 lambda表达式list操作分组、过滤、求和、最值、排序、去重