Java8 新特性之集合操作Stream

Stream简介

  • Java 8引入了全新的Stream API。这里的Stream和I/O流不同,它更像具有Iterable的集合类,但行为和集合类又有所不同。
  • stream是对集合对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作,或者大批量数据操作。

为什么要使用Stream

  • 函数式编程带来的好处尤为明显。这种代码更多地表达了业务逻辑的意图,而不是它的实现机制。易读的代码也易于维护、更可靠、更不容易出错。
  • 高端

使用实例:

测试数据:

public class Data {
private static List<PersonModel> list = null; static {
PersonModel wu = new PersonModel("wu qi", 18, "男");
PersonModel zhang = new PersonModel("zhang san", 19, "男");
PersonModel wang = new PersonModel("wang si", 20, "女");
PersonModel zhao = new PersonModel("zhao wu", 20, "男");
PersonModel chen = new PersonModel("chen liu", 21, "男");
list = Arrays.asList(wu, zhang, wang, zhao, chen);
} public static List<PersonModel> getData() {
return list;
}
}

Filter

  • 遍历数据并检查其中的元素时使用。
  • filter接受一个函数作为参数,该函数用Lambda表达式表示。
保留年龄为 20 的 person 元素
list = list.stream()
.filter(person -> person.getAge() == 20)
.collect(toList()); 打印输出 [Person{name='jack', age=20}]
   /**
* 过滤所有的男性
*/
public static void fiterSex(){
List<PersonModel> data = Data.getData(); //old
List<PersonModel> temp=new ArrayList<>();
for (PersonModel person:data) {
if ("男".equals(person.getSex())){
temp.add(person);
}
}
System.out.println(temp);
//new
List<PersonModel> collect = data
.stream()
.filter(person -> "男".equals(person.getSex()))
.collect(toList());
System.out.println(collect);
} /**
* 过滤所有的男性 并且小于20岁
*/
public static void fiterSexAndAge(){
List<PersonModel> data = Data.getData(); //old
List<PersonModel> temp=new ArrayList<>();
for (PersonModel person:data) {
if ("男".equals(person.getSex())&&person.getAge()<20){
temp.add(person);
}
} //new 1
List<PersonModel> collect = data
.stream()
.filter(person -> {
if ("男".equals(person.getSex())&&person.getAge()<20){
return true;
}
return false;
})
.collect(toList());
//new 2
List<PersonModel> collect1 = data
.stream()
.filter(person -> ("男".equals(person.getSex())&&person.getAge()<20))
.collect(toList()); }

distinct()

去除重复元素,这个方法是通过类的 equals 方法来判断两个元素是否相等的

如例子中的 Person 类,需要先定义好 equals 方法,不然类似[Person{name='jack', age=20}, Person{name='jack', age=20}] 这样的情况是不会处理的

参考:https://blog.csdn.net/haiyoung/article/details/80934467

 

limit(long n)

limit: 对一个Stream进行截断操作,获取其前N个元素,如果原Stream中包含的元素个数小于N,那就获取其所有的元素;

返回前 n 个元素

list = list.stream()
.limit(2)
.collect(toList()); 打印输出 [Person{name='jack', age=20}, Person{name='mike', age=25}]

skip方法 :
skip: 返回一个丢弃原Stream的前N个元素后剩下元素组成的新Stream,如果原Stream中包含的元素个数小于N,那么返回空Stream;

Map

  • map生成的是个一对一映射,for的作用
  • 比较常用
   /**
* 取出所有的用户名字
*/
public static void getUserNameList(){
List<PersonModel> data = Data.getData(); //old
List<String> list=new ArrayList<>();
for (PersonModel persion:data) {
list.add(persion.getName());
}
System.out.println(list); //new 1
List<String> collect = data.stream().map(person -> person.getName()).collect(toList());
System.out.println(collect); //new 2
List<String> collect1 = data.stream().map(PersonModel::getName).collect(toList());
System.out.println(collect1); //new 3
List<String> collect2 = data.stream().map(person -> {
System.out.println(person.getName());
return person.getName();
}).collect(toList());
}

filter 与 map 同时使用: 

 List<String> collect = users.stream().filter(item -> {
if (!StringUtils.isEmpty(item.getUserName())) {
return true;
}
return false;
}).map(item -> item.getUserName()).collect(Collectors.toList());

FlatMap

  • 顾名思义,跟map差不多,更深层次的操作

  • 但还是有区别的

  • map和flat返回值不同

  • Map 每个输入元素,都按照规则转换成为另外一个元素。
    还有一些场景,是一对多映射关系的,这时需要 flatMap。

  • Map一对一

  • Flatmap一对多

  • map和flatMap的方法声明是不一样的

    • <r> Stream<r> map(Function mapper);
    • <r> Stream<r> flatMap(Function> mapper);
  • map和flatMap的区别:我个人认为,flatMap的可以处理更深层次的数据,入参为多个list,结果可以返回为一个list,而map是一对一的,入参是多个list,结果返回必须是多个list。通俗的说,如果入参都是对象,那么flatMap可以操作对象里面的对象,而map只能操作第一层。

 
public static void flatMapString() {
List<PersonModel> data = Data.getData();
//返回类型不一样
List<String> collect = data.stream()
.flatMap(person -> Arrays.stream(person.getName().split(" "))).collect(toList()); List<Stream<String>> collect1 = data.stream()
.map(person -> Arrays.stream(person.getName().split(" "))).collect(toList()); //用map实现
List<String> collect2 = data.stream()
.map(person -> person.getName().split(" "))
.flatMap(Arrays::stream).collect(toList());
//另一种方式
List<String> collect3 = data.stream()
.map(person -> person.getName().split(" "))
.flatMap(str -> Arrays.asList(str).stream()).collect(toList());
}

 map转list:

 Map<String, List<ProgrammeResult>> projectGroups = programmeResults.stream().collect(Collectors.groupingBy(ProgrammeResult::getProjectId));

 List<ProgrammeResult> fhSuccessResult = projectGroups.entrySet().stream().flatMap(item -> item.getValue().stream()).collect(Collectors.toList());

Collect

  • collect在流中生成列表,map,等常用的数据结构
  • toList()
  • toSet()
  • toMap()
 /**
* toList
*/
public static void toListTest(){
List<PersonModel> data = Data.getData();
List<String> collect = data.stream()
.map(PersonModel::getName)
.collect(Collectors.toList());
} /**
* toSet
*/
public static void toSetTest(){
List<PersonModel> data = Data.getData();
Set<String> collect = data.stream()
.map(PersonModel::getName)
.collect(Collectors.toSet());
} /**
* toMap
*/
public static void toMapTest(){
List<PersonModel> data = Data.getData();
Map<String, Integer> collect = data.stream()
.collect(
Collectors.toMap(PersonModel::getName, PersonModel::getAge)
); data.stream()
.collect(Collectors.toMap(per->per.getName(), value->{
return value+"1";
}));
} /**
* 指定类型
*/
public static void toTreeSetTest(){
List<PersonModel> data = Data.getData();
TreeSet<PersonModel> collect = data.stream()
.collect(Collectors.toCollection(TreeSet::new));
System.out.println(collect);
} /**
* 分组
*/
public static void toGroupTest(){
List<PersonModel> data = Data.getData();
Map<Boolean, List<PersonModel>> collect = data.stream()
.collect(Collectors.groupingBy(per -> "男".equals(per.getSex())));
System.out.println(collect);
} /**
* 分隔
*/
public static void toJoiningTest(){
List<PersonModel> data = Data.getData();
String collect = data.stream()
.map(personModel -> personModel.getName())
.collect(Collectors.joining(",", "{", "}"));
System.out.println(collect);
}

groupingBy 分组

groupingBy 用于将数据分组,最终返回一个 Map 类型

Map<Integer, List<Person>> map = list.stream().collect(groupingBy(Person::getAge));

例子中我们按照年龄 age 分组,每一个 Person 对象中年龄相同的归为一组

另外可以看出,Person::getAge 决定 Map 的键(Integer 类型),list 类型决定 Map 的值(List)

2.收集对象实体本身
- 在开发过程中我们也需要有时候对自己的list中的实体按照其中的一个字段进行分组(比如 id ->List),这时候要设置map的value值是实体本身。

public Map<Long, Account> getIdAccountMap(List<Account> accounts) {
return accounts.stream().collect(Collectors.toMap(Account::getId, account -> account));
}

account -> account是一个返回本身的lambda表达式,其实还可以使用Function接口中的一个默认方法 Function.identity(),这个方法返回自身对象,更加简洁

重复key的情况。
在list转为map时,作为key的值有可能重复,这时候流的处理会抛出个异常:Java.lang.IllegalStateException:Duplicate key。这时候就要在toMap方法中指定当key冲突时key的选择。(这里是选择第二个key覆盖第一个key)

public Map<String, Account> getNameAccountMap(List<Account> accounts) {
return accounts.stream().collect(Collectors.toMap(Account::getUsername, Function.identity(), (key1, key2) -> key2));
}

分组后统计每个组的数量:

Map<Integer, Long> items = list.stream().collect(Collectors.groupingBy(User::getUserName,Collectors.counting()));

  

多级分组

groupingBy 可以接受一个第二参数实现多级分组:

Map<Integer, Map<T, List<Person>>> map = list.stream().collect(groupingBy(Person::getAge, groupBy(...)));

partitioningBy 分区

分区与分组的区别在于,分区是按照 true 和 false 来分的,因此partitioningBy 接受的参数的 lambda 也是 T -> boolean

根据年龄是否小于等于20来分区
Map<Boolean, List<Person>> map = list.stream()
.collect(partitioningBy(p -> p.getAge() <= 20)); 打印输出
{
false=[Person{name='mike', age=25}, Person{name='tom', age=30}],
true=[Person{name='jack', age=20}]
}

【统计】

  List<User> users = User.getUsers();
int sum = users.stream().mapToInt(User::getUserAge).sum();//求和
System.out.println("sum==" + sum);
int max = users.stream().mapToInt(User::getUserAge).max().getAsInt();//最大
System.out.println("max==" + max);
int min = users.stream().mapToInt(User::getUserAge).min().getAsInt();//最小
System.out.println("min==" + min);
Double average = users.stream().mapToInt(User::getUserAge).average().getAsDouble();//平均值
System.out.println("average==" + average);
long count = users.stream().mapToInt(User::getUserAge).count(); // 得到元素个数
System.out.println("count===" + count);
【参数匹配】

 // allMatch 检测是否全部满足指定的参数行为
boolean b = users.stream().allMatch(User->User.getUserAge()>5);
System.out.println("allMatch,检测是否全部都满足指定的参数行为:"+b);
// anyMatch 检测是否存在一个或者多个满足指定的参数行为
boolean any = users.stream().anyMatch(User->User.getUserAge()>5);
System.out.println("anyMatch,检测是否存在一个或多个满足指定的参数行为:"+any);
// nonMatch 检测是否不存在满足指定行为的元素
boolean non = users.stream().noneMatch(User->User.getUserAge()>5);
System.out.println("检测是否不存在满足指定行为的元素:"+non);



参考博客:

https://www.ibm.com/developerworks/cn/java/j-lo-java8streamapi/

https://www.jianshu.com/p/9fe8632d0bc2

https://cloud.tencent.com/developer/article/1187833

https://www.concretepage.com/java/jdk-8/java-8-distinct-example

Java8 新特性之集合操作Stream的更多相关文章

  1. JAVA8新特性--集合流操作Stream

    原文链接:https://blog.csdn.net/bluuusea/article/details/79967039 Stream类全路径为:java.util.stream.Stream 对St ...

  2. Java8 新特性之Stream----java.util.stream

    这个包主要提供元素的streams函数操作,比如对collections的map,reduce. 例如: int sum = widgets.stream() .filter(b -> b.ge ...

  3. [Java8教程]Java8新特性进阶集合

    Java8新特性进阶集合 基于 AOP 抽离方法的重复代码 Java8:当 Lambda 遇上受检异常 Java8:对字符串连接的改进 Java8:Java8 中 Map 接口的新方法 Java8:当 ...

  4. Java8新特性之方法引用&Stream流

    Java8新特性 方法引用 前言 什么是函数式接口 只包含一个抽象方法的接口,称为函数式接口. 可以通过 Lambda 表达式来创建该接口的对象.(若 Lambda 表达式抛出一个受检异常(即:非运行 ...

  5. Java8新特性(1)—— Stream集合运算流入门学习

    废话,写在前面 好久没写博客了,懒了,以后自觉写写博客,每周两三篇吧! 简单记录自己的学习经历,算是对自己的一点小小的督促! Java8的新特性很多,比如流处理在工作中看到很多的地方都在用,是时候扔掉 ...

  6. Java8新特性第3章(Stream API)

    Stream作为Java8的新特性之一,他与Java IO包中的InputStream和OutputStream完全不是一个概念.Java8中的Stream是对集合功能的一种增强,主要用于对集合对象进 ...

  7. Java8 新特性2——强大的Stream API

    强大的Stream API Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找.过滤和映射数据等操作.简而言之,Stream API 提供 ...

  8. java8新特性-lambda表达式和stream API的简单使用

    一.为什么使用lambda Lambda 是一个 匿名函数,我们可以把 Lambda表达式理解为是 一段可以传递的代码(将代码像数据一样进行传递).可以写出更简洁.更灵活的代码.作为一种更紧凑的代码风 ...

  9. Java8 新特性 Stream 无状态中间操作

    无状态中间操作 Java8 新特性 Stream 练习实例 中间无状态操作,可以在单个对单个的数据进行处理.比如:filter(过滤)一个元素的时候,也可以判断,比如map(映射)... 过滤 fil ...

随机推荐

  1. 运维DNS原理配置

    Linux DNS原理简介及配置 DNS简介 DNS原理 域名解析的过程 资源记录 DNS BIND安装配置 一.简介 一般来讲域名比IP地址更加的有含义.也更容易记住,所以通常用户更习惯输入域名来访 ...

  2. 编写优秀 CSS 代码的 8 个策略

    编写基本的CSS和HTML是我们作为Web开发人员学习的首要事情之一.然而,我遇到的很多应用程序显然没有人花时间真正考虑前端开发的长久性和可维护性. 我认为这主要是因为许多开发人员对组织CSS / H ...

  3. vscode如何使用?常用插件有哪些?

    vscode下载 官网下载:https://code.visualstudio.com/ 一.汉化中文(官方下载默认为英文,英文好的小伙伴可直接跳过这步) 点击插件按钮搜索 Chinese, 在弹出的 ...

  4. Genymotion模拟器使用camera

    1.前言 最近开发react-native的app,上传图片功能需要使用相机,发现Genymotion默认的相机不工作.查看同行的博客解决了,归纳整理一下. 2.步骤 2.1安装Genymotion: ...

  5. 003-基于impi zabbix监控r720 测试过程

    1.F2进入服务器bios 修改network  使这台服务器能够被远程访问. 2.在远程的centos 7 服务器上安装  impitool工具包 #ipmitool -I lanplus -H X ...

  6. 3D绘图计算器(geogebra[5.0.385.0])使用QQ浏览器打开下载

    点击这里下载3D绘图计算器

  7. Django ckeditor增加编辑代码 功能

    前言 使用ckeditor这个组件的时候 对于长写博客的同学当然希望能有 增加代码这个功能按钮 而这个按钮 需要自己配置 我们的编辑器自然需要添加代码块的功能. 需要用到插件codesnippet,c ...

  8. jQuery后续和 前端框架Bootstrap

    目录 一.jQuery后续 1. 动画效果 (1)自定义点赞动画实例 2. jQuery的自带方法 (1)each (类似for循环) (2)data() (存放隐形的数据) 二.前端框架之Boots ...

  9. java面向对象1-面向对象概念

    面向对象概念-类与对象的关系 封装:指隐藏对象的属性和实现细节,仅对外提供公共访问方式,private-构造方法/构造器-this关键字-static关键字(javadoc制作工具类) -代码块 继承 ...

  10. 《Head First 软件开发》阅读二

    项目计划:为成功而筹划 每段伟大的代码始于伟大的计划. 客户现在就要他们的软件,可是开发需要的时间远远超过客户要求时间.我们需要实际解决方法:由客户确定优先级,与客户一起确定优先级顺序,开发出Mile ...