Java8新特性之Steam流详解
流的操作包含如下三个部分:创建流、中间流、关闭流,筛选
、去重
、映射
、排序
属于流的中间操作,收集
属于终止操作。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());
filter和map操作通常结合使用,取出流中某行某列的数据,建议先行后列的方式定位。
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
的应用场景更为强大,下面对这个场景进行详细介绍。希望返回结果中能够建立ID
与NAME
之间的匹配关系,最常见的场景是通过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));
ID
与Object
类映射
@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流详解相关推荐
- 【java8新特性】——Stream API详解(二)
一.简介 java8新添加了一个特性:流Stream.Stream让开发者能够以一种声明的方式处理数据源(集合.数组等),它专注于对数据源进行各种高效的聚合操作(aggregate operation ...
- java8 stream遍历_Java8新特性:Stream流详解
1. Stream初体验 我们先来看看Java里面是怎么定义Stream的: A sequence of elements supporting sequential and parallel agg ...
- java8新特性之Steam
java8新特性之Steam 一.什么是Stream?传统集合遍历循环存在哪些弊端? 二.获取流的方式 2.1.根据Collection获取流 2.2.根据Map获取流 2.3.根据数组获取流 三.S ...
- 使用Java8新特性(stream流、Lambda表达式)实现多个List 的笛卡尔乘积 返回需要的List<JavaBean>
需求分析: 有两个Long类型的集合 : List<Long> tagsIds; List<Long> attributesIds; 现在需要将这两个Long类型的集合进行组合 ...
- Spark 3.2.0 版本新特性 push-based shuffle 论文详解(一)概要和介绍
前言 本文隶属于专栏<大数据技术体系>,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢! 本专栏目录结构和参考文献请见大数据技术体系 目录 Spark 3.2.0 ...
- Spark 3.2.0 版本新特性 push-based shuffle 论文详解(二)背景和动机
前言 本文隶属于专栏<大数据技术体系>,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢! 本专栏目录结构和参考文献请见大数据技术体系 目录 Spark 3.2.0 ...
- java stream byte_乐字节-Java8新特性之Stream流(上)
上一篇文章,小乐给大家介绍了<Java8新特性之方法引用>,下面接下来小乐将会给大家介绍Java8新特性之Stream,称之为流,本篇文章为上半部分. 1.什么是流? Java Se中对于 ...
- Java8新特性-使用Stream流来实现递归遍历树形结构(案例)
Java8新特性-Stream流 可能平常会遇到一些需求,比如构建菜单,构建树形结构,数据库一般就使用父id来表示,为了降低数据库的查询压力,我们可以使用Java8中的Stream流一次性把数据查出来 ...
- java8新特性之Steam(告别繁琐的循环遍历)
目录 一.什么是Stream?传统集合遍历循环存在哪些弊端? 二.Stream提供更优的写法 三.获取流的方式 1.根据Collection获取流 2.根据Map获取流 3.根据数组获取流 四.Str ...
最新文章
- linux下c语言写文件,Linux下C语言之文件操作
- iPod/iTouch/ipad/iPhone新手注意事项
- 根据鼠标点击位置获取DataGridView的选择行号。
- 解决内存瓶颈和计算负载问题,韩松团队提出 MCUNetV2
- c语言坐标三角形判断,【C语言】判断三角形类型(示例代码)
- NOIP2017普及组翻车记
- Writing your first Django app--Django 第一步
- “人肉”背后隐藏的网络风险
- Ubuntu 16.04安装Chrome浏览器
- 面试题 17.10. 主要元素
- pyecharts anaconda_Pyecharts安装使用和绘图案例
- 计算机网络之物理层:6、传输介质
- 声明式事务、Spring 中常用注解、Ajax
- 访问控制 > 教程 > 利用标签对ECS实例进行分组授权
- PHP上传大文件 分割文件上传
- IP核授权形式:软核、固核和硬核
- 计算机机房综合布线方案,计算机网络设备施工方案(不含综合布线)(word版)
- Aspose.Words doc转pdf 内容出现丢失,页码跳页,排版混乱问题
- 百度热力图颜色说明_揭秘!张家口100万人口热力图,看完你就知道房子该买哪里了...
- python flask热更新_客户端python热更新