流的操作包含如下三个部分:创建流、中间流、关闭流,筛选去重映射排序属于流的中间操作,收集属于终止操作。Stream是流操作的基础关键类。

一、创建流

(1)通过集合创建流

// 通过集合创建流
List<String> lists = new ArrayList<>();
lists.stream();

(2)通过数组创建流

// 通过数组创建流
String[] strings = new String[5];
Stream.of(strings);

二、日常操作

(1)筛选

筛选是指从(集合)流中筛选满足条件的子集,通过 Lambda 表达式生产型接口来实现。

// 通过断言型接口实现元素的过滤
stream.filter(x->x.getId()>10);

非空过滤

非空过滤包含两层内容:

一是当前对象是否为空或者非空;

二是当前对象的某属性是否为空或者非空。

筛选非空对象,语法stream.filter(Objects::nonNull)做非空断言。

// 非空断言
java.util.function.Predicate<Boolean> nonNull = Objects::nonNull;

(2)去重

去重是指将(集合)流中重复的元素去除,通过 hashcode 和 equals 函数来判断是否是重复元素。去重操作实现了类似于 HashSet 的运算,对于对象元素流去重,需要重写 hashcode 和 equals 方法。

如果流中泛型对象使用 Lombok 插件,使用@Data注解默认重写了 hashcode 和 equals 方法,字段相同并且属性相同,则对象相等。

stream.distinct();

(3)映射

取出流中元素的某一列,然后配合收集以形成新的集合。

stream.map(x->x.getId());

filtermap操作通常结合使用,取出流中某行某列的数据,建议先行后列的方式定位。

Optional<MainExportModel> model = data.stream().filter(e -> e.getId().equals(Id)).findFirst();
if (model.isPresent()) {String itemName = model.get().getItemName();String itemType = model.get().getItemType();return new MainExportVo(itemId, itemName);
}

(4)排序

传统的Collectors类中的排序支持 List 实现类中的一部分排序,使用 stream 排序,能够覆盖所有的 List 实现类。

// 按照默认字典顺序排序
stream.sorted();
// 按照sortNo排序
stream.sorted((x,y)->Integer.compare(x.getSortNo(),y.getSortNo()));

1、函数式接口排序

基于 Comparator 类中函数式方法,能够更加优雅的实现对象流的排序。

// 正向排序(默认)
pendingPeriod.stream().sorted(Comparator.comparingInt(ReservoirPeriodResult::getId));
// 逆向排序
pendingPeriod.stream().sorted(Comparator.comparingInt(ReservoirPeriodResult::getId).reversed());

2、LocalDate 和 LocalDateTime 排序

新日期接口相比就接口,使用体验更加,因此越来越多的被应用,基于日期排序是常见的操作。

// 准备测试数据
Stream<DateModel> stream = Stream.of(new DateModel(LocalDate.of(2020, 1, 1))
, new DateModel(LocalDate.of(2021, 1, 1)), new DateModel(LocalDate.of(2022, 1, 1)));

正序、逆序排序

// 正向排序(默认)
stream.sorted(Comparator.comparing(DateModel::getLocalDate))
.forEach(System.out::println);
// 逆向排序
stream.sorted(Comparator.comparing(DateModel::getLocalDate).reversed())
.forEach(System.out::println);

(5)规约(reduce)

对流中的元素按照一定的策略计算。终止操作的底层逻辑都是由 reduce 实现的。

三、终止操作

收集(collect)将流中的中间(计算)结果存储到集合中,方便后续进一步使用。

1、普通收集

(1)收集为List

默认返回的类型为ArrayList,可通过Collectors.toCollection(LinkedList::new)显示指明使用其它数据结构作为返回值容器。

List<String> collect = stream.collect(Collectors.toList());

由集合创建流的收集需注意:仅仅修改流字段中的内容,没有返回新类型,如下操作直接修改原始集合,无需处理返回值。

// 直接修改原始集合
userVos.stream().map(e -> e.setDeptName(hashMap.get(e.getDeptId())))
.collect(Collectors.toList());

(2)收集为Set

默认返回类型为HashSet,可通过Collectors.toCollection(TreeSet::new)显示指明使用其它数据结构作为返回值容器。

Set<String> collect = stream.collect(Collectors.toSet());

2、高级收集

(1)收集为Map

默认返回类型为HashMap,可通过Collectors.toCollection(LinkedHashMap::new)显示指明使用其它数据结构作为返回值容器。

收集为Map的应用场景更为强大,下面对这个场景进行详细介绍。希望返回结果中能够建立IDNAME之间的匹配关系,最常见的场景是通过ID批量到数据库查询NAME,返回后再将原数据集中的ID替换成NAME

ID 到 NAME 映射

@Data
public class Entity {private Integer id;private String name;
}

准备集合数据,此部分通常是从数据库查询的数据

// 模拟从数据库中查询批量的数据
List<Entity> entityList = Stream.of(new Entity(1,"A"),
new Entity(2,"B"), new Entity(3,"C")).collect(Collectors.toList());

将集合数据转化成 ID 与 NAME 的 Map

// 将集合数据转化成ID与NAME的Map
Map<Integer, String> hashMap = entityList.stream()
.collect(Collectors.toMap(Entity::getId, Entity::getName));

IDObject类映射

@Data
public class Entity {private Integer id;private String name;private Boolean status;
}

将集合数据转化成 ID 与实体类的 Map

// 将集合数据转化成ID与实体类的Map
Map<Integer, ItemEntity> hashMap =
entityList.stream().collect(Collectors.toMap(ItemEntity::getItemId, e -> e));

其中Collectors类中的toMap参数是函数式接口参数,能够自定义返回值。

public static <T, K, U> Collector<T, ?, Map<K,U>> toMap
(Function<? super T, ? extends K> keyMapper,Function<? super T, ? extends U> valueMapper)
{return toMap(keyMapper, valueMapper, throwingMerger(), HashMap::new);
}

(2)分组收集

流的分组收集操作在内存层次模拟了数据库层面的group by操作,下面演示流的分组操作。Collectors类提供了各种层次的分组操作支撑。

流的分组能力对应数据库中的聚合函数,目前大部分能在数据库中操作的聚合函数,都能在流中找到相应的能力。

// 默认使用List作为分组后承载容器
Map<Integer, List<XUser>> hashMap =
xUsers.stream().collect(Collectors.groupingBy(XUser::getDeptId));// 显示指明使用List作为分组后承载容器
Map<Integer, List<XUser>> hashMap =
xUsers.stream().collect(Collectors.groupingBy(XUser::getDeptId, Collectors.toList()));

映射后再分组

Map<Integer, List<String>> hashMap = xUsers.stream().collect(Collectors.groupingBy(XUser::getDeptId,Collectors.mapping(XUser::getUserName,Collectors.toList())));

四、Steam拓展

(一)集合与对象互转

将对象包装成集合的形式和将集合拆解为对象的形式是常见的操作。

1、对象转集合

/*** 将单个对象转化为集合** @param t   对象实例* @param <T> 对象类型* @param <C> 集合类型* @return 包含对象的集合实例*/
public static <T, C extends Collection<T>> Collection<T> toCollection(T t) {return toCollection(t, ArrayList::new);
}

用户自定义返回的集合实例类型

/*** 将单个对象转化为集合** @param t        对象实例* @param supplier 集合工厂* @param <T>      对象类型* @param <C>      集合类型* @return 包含对象的集合实例*/
public static <T, C extends Collection<T>> Collection<T> toCollection(T t, Supplier<C> supplier) {return Stream.of(t).collect(Collectors.toCollection(supplier));
}

2、集合转对象

使用默认的排序规则,注意此处不是指自然顺序排序。

/*** 取出集合中第一个元素** @param collection 集合实例* @param <E>        集合中元素类型* @return 泛型类型*/
public static <E> E toObject(Collection<E> collection) {// 处理集合空指针异常Collection<E> coll = Optional.ofNullable(collection).orElseGet(ArrayList::new);// 此处可以对流进行排序,然后取出第一个元素return coll.stream().findFirst().orElse(null);
}

上述方法巧妙的解决两个方面的异常问题:

一是集合实例引用空指针异常;

二是集合下标越界异常。

(二)其他

1、并行计算

基于流式计算中的并行流,能够显著提高大数据下的计算效率,充分利用 CPU 核心数。

// 通过并行流实现数据累加
LongStream.rangeClosed(1,9999999999999999L).parallel().reduce(0,Long::sum);

2、序列数组

生成指定序列的数组或者集合。

// 方式一:生成数组
int[] ints = IntStream.rangeClosed(1, 100).toArray();
// 方式二:生成集合
List<Integer> list = Arrays.stream(ints).boxed().collect(Collectors.toList());

六、流的应用Demo

列表转树

传统方式下构建树形列表需要反复递归调用查询数据库,效率偏低。对于一棵结点较多的树,效率更低。这里提供一种只需调用一次数据库,通过流将列表转化为树的解决方式。

/*** 列表转树** @param rootList     列表的全部数据集* @param parentId 第一级目录的父ID* @return 树形列表*/
public List<IndustryNode> getChildNode(List<Industry> rootList, String parentId) {List<IndustryNode> lists = rootList.stream().filter(e -> e.getParentId().equals(parentId)).map(IndustryNode::new).collect(toList());lists.forEach(e -> e.setChilds(getChildNode(rootList, e.getId())));return lists;
}

Java8新特性之Steam流详解相关推荐

  1. 【java8新特性】——Stream API详解(二)

    一.简介 java8新添加了一个特性:流Stream.Stream让开发者能够以一种声明的方式处理数据源(集合.数组等),它专注于对数据源进行各种高效的聚合操作(aggregate operation ...

  2. java8 stream遍历_Java8新特性:Stream流详解

    1. Stream初体验 我们先来看看Java里面是怎么定义Stream的: A sequence of elements supporting sequential and parallel agg ...

  3. java8新特性之Steam

    java8新特性之Steam 一.什么是Stream?传统集合遍历循环存在哪些弊端? 二.获取流的方式 2.1.根据Collection获取流 2.2.根据Map获取流 2.3.根据数组获取流 三.S ...

  4. 使用Java8新特性(stream流、Lambda表达式)实现多个List 的笛卡尔乘积 返回需要的List<JavaBean>

    需求分析: 有两个Long类型的集合 : List<Long> tagsIds; List<Long> attributesIds; 现在需要将这两个Long类型的集合进行组合 ...

  5. Spark 3.2.0 版本新特性 push-based shuffle 论文详解(一)概要和介绍

    前言 本文隶属于专栏<大数据技术体系>,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢! 本专栏目录结构和参考文献请见大数据技术体系 目录 Spark 3.2.0 ...

  6. Spark 3.2.0 版本新特性 push-based shuffle 论文详解(二)背景和动机

    前言 本文隶属于专栏<大数据技术体系>,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢! 本专栏目录结构和参考文献请见大数据技术体系 目录 Spark 3.2.0 ...

  7. java stream byte_乐字节-Java8新特性之Stream流(上)

    上一篇文章,小乐给大家介绍了<Java8新特性之方法引用>,下面接下来小乐将会给大家介绍Java8新特性之Stream,称之为流,本篇文章为上半部分. 1.什么是流? Java Se中对于 ...

  8. Java8新特性-使用Stream流来实现递归遍历树形结构(案例)

    Java8新特性-Stream流 可能平常会遇到一些需求,比如构建菜单,构建树形结构,数据库一般就使用父id来表示,为了降低数据库的查询压力,我们可以使用Java8中的Stream流一次性把数据查出来 ...

  9. java8新特性之Steam(告别繁琐的循环遍历)

    目录 一.什么是Stream?传统集合遍历循环存在哪些弊端? 二.Stream提供更优的写法 三.获取流的方式 1.根据Collection获取流 2.根据Map获取流 3.根据数组获取流 四.Str ...

最新文章

  1. linux下c语言写文件,Linux下C语言之文件操作
  2. iPod/iTouch/ipad/iPhone新手注意事项
  3. 根据鼠标点击位置获取DataGridView的选择行号。
  4. 解决内存瓶颈和计算负载问题,韩松团队提出 MCUNetV2
  5. c语言坐标三角形判断,【C语言】判断三角形类型(示例代码)
  6. NOIP2017普及组翻车记
  7. Writing your first Django app--Django 第一步
  8. “人肉”背后隐藏的网络风险
  9. Ubuntu 16.04安装Chrome浏览器
  10. 面试题 17.10. 主要元素
  11. pyecharts anaconda_Pyecharts安装使用和绘图案例
  12. 计算机网络之物理层:6、传输介质
  13. 声明式事务、Spring 中常用注解、Ajax
  14. 访问控制 > 教程 > 利用标签对ECS实例进行分组授权
  15. PHP上传大文件 分割文件上传
  16. IP核授权形式:软核、固核和硬核
  17. 计算机机房综合布线方案,计算机网络设备施工方案(不含综合布线)(word版)
  18. Aspose.Words doc转pdf 内容出现丢失,页码跳页,排版混乱问题
  19. 百度热力图颜色说明_揭秘!张家口100万人口热力图,看完你就知道房子该买哪里了...
  20. python flask热更新_客户端python热更新

热门文章

  1. 服务器配置PPTP-VNP
  2. 人生不能重来,于是有了电影
  3. PS 滤镜——极坐标变换到平面坐标
  4. 2021年Linux界的十二件大事
  5. 三星java3倍拍照手机_三星史上拍照最强后置三摄手机,果不其然:这次让你们欣赏大片...
  6. PHP,HTML里面的EQ、NE、GT、LT、GE、LE分别代表含义
  7. 【2023最新】超详细图文保姆级教程:App开发新手入门(4)
  8. Word2000 的命令ID,识货的进
  9. VC实现SMC加密技术
  10. 2019杭州云栖大会掠影:进击的阿里云