Java8 函数式编程stream流

这篇具有很好参考价值的文章主要介绍了Java8 函数式编程stream流。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

开篇介绍

Java 8 中新增的特性旨在帮助程序员写出更好的代码,其中对核心类库的改进是很关键的一部分,也是本章的主要内容。对核心类库的改进主要包括集合类的 API 和新引入的流(Stream),流使程序员得以站在更高的抽象层次上对集合进行操作。下面将介绍stream流的用法。

1.初始环境准备

​ 场景:现在有一个公司,公司部门有一级部门,二级部门甲和二级部门乙(其中二级部门甲和二级部门乙是一级部门的子部门),

一级部门下面有有001号员工小明,二级部门甲下面有002号员工小刚和003号员工小李,二级部门乙有002号员工小刚和004号员工小张,其中员工id是唯一的,员工小刚既是二级部门甲又是二级部门乙的员工。代码展示如下:

public class LambdaUseCase {
    static List<Department> departmentList;
    static {
        // 一级部门,部门人员有001号员工小明
        Department departmentOne = new Department("一级部门", 1,10000L,11000L,
                Arrays.asList(new Person("001","小明",22)));
        // 二级部门甲,部门人员有002号员工小刚和003号员工小李
        Department departmentTwoFirst = new Department("二级部门甲", 2,8000L,13000L,
                Arrays.asList(new Person("002","小刚",23),
                        new Person("003","小李",32)));
        // 二级部门乙,部门人员有002号员工小刚和004号员工小张
        Department departmentTwoSecond = new Department("二级部门已", 2,7500L,15000L,
                Arrays.asList(new Person("002","小刚",23),
                        new Person("004","小张",34)));

        departmentList = Arrays.asList(departmentOne,departmentTwoFirst,departmentTwoSecond);
    }
}

@Data
@AllArgsConstructor
@NoArgsConstructor
class Department {
    // 部门名
    private String departmentName;
    // 部门等级
    private Integer departmenRank;
    // 部门薪资(单位分)
    private Long departSalary;
    // 部门日盈利
    private Long departProfit;
    // 部门人员集合
    private List<Person> persons;
}

@Data
@AllArgsConstructor
@NoArgsConstructor
//重写equal和hashcode方法,用于数据去重
@EqualsAndHashCode
class Person {
    // 人员id
    private String personId;
    // 人员姓名
    private String personName;
    // 人员年龄
    private Integer personAge;
}

2.创建流

单列集合: 集合对象.stream() 创建流

departmentList.stream();

数组:Arrays.stream(数组) 或者使用Stream.of来创建

Integer[] arr = {1,2,3,4,5};
Stream<Integer> stream = Arrays.stream(arr);
Stream<Integer> stream2 = Stream.of(arr);
Stream.of(1,2,3,4);

双列集合:转换成单列集合后再创建

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

3.中间操作

3.1 map

如果有一个函数可以将一种类型的值转换成另外一种类型,map 操作就可以使用该函数,将一个流中的值转换成一个新的流

需求:公司今年收益提供,决定把所有部门的平均薪资提升1000。

List<Department> departments = departmentList.stream().map(e -> {
    e.setDepartSalary(e.getDepartSalary() + 1000);
    return e;
}).collect(Collectors.toList());

3.2 filter

对流中的元素进行过滤,筛选出符合过滤条件的数据

需求:需要筛选出部门平均薪资大于等于8000的数据。

List<Department> departments = departmentList.stream()
		.filter(e -> e.getDepartSalary() >= 8000)
		.collect(Collectors.toList());

3.3 flatMap

flatMap 方法可用 Stream 替换值,然后将多个 Stream 连接成一个 Stream

需求:把公司所有的人员收集到一个集合中。

不使用flatMap,你可能会这么做,循环里套循环,看上去不太美观。

Set<Person> personSet = new HashSet<>();
departmentList.stream().forEach(e->{
	e.getPersons().stream().forEach(person->{
		personSet.add(person);
	});
});

使用flatMap写法。

Set<Person> personSet = departmentList.stream()
        .flatMap(e -> e.getPersons().stream())
        .collect(Collectors.toSet());

3.4 distinct

去除流中的重复元素

需求:将公司所有的人员统计出来,这回不使用Set去重,注意:002号员工即在二级部门甲又在二级部门乙

List<Person> personSet = departmentList.stream()
        .flatMap(e -> e.getPersons().stream())
        .distinct()
        .collect(Collectors.toList());

注意:distinct方法是依赖Object的equals方法来判断是否是相同对象的。所以需要注意重写equals方法。

3.5 sorted

对流中的元素进行排序

需求: 按薪资从低到高将部门列出来。

List<Department> departments = departmentList.stream()
        .sorted((o1, o2) -> o1.getDepartSalary().compareTo(o2.getDepartSalary()))
        .collect(Collectors.toList());

注意:如果调用空参的sorted()方法,需要流中的元素是实现了Comparable。

3.6 limit

可以设置流的最大长度,超出的部分将被抛弃

需求:按薪资从低到高将部门列出来,并且找出薪资最低的俩个部门

List<Department> departments = departmentList.stream()
		.sorted(Comparator.comparing(Department::getDepartSalary))
		.limit(2)
		.collect(Collectors.toList());

3.7 skip

跳过流中的前n个元素,返回剩下的元素

需求:按薪资从低到高将部门列出来,并且忽略薪资最低的部门

List<Department> departments = departmentList.stream()
		.sorted(Comparator.comparing(Department::getDepartSalary))
		.skip(1)
		.collect(Collectors.toList());

4.终结操作

4.1 foreach

对流中的元素进行遍历操作

需求:要求输出全部部门的名称

departmentList.stream()
                .forEach(e -> System.out.println(e.getDepartmentName()));

4.2 count

可以用来获取当前流中元素的个数。

需求:统计部门的格式

long count = departmentList.stream()
    .count();

4.3 max和min

Stream 上常用的操作之一是求最大值和最小值

需求:分别找出部门里面薪资最高的部门和最低的部门

Department maxDepartment = departmentList.stream()
		.max(Comparator.comparing(Department::getDepartSalary))
		.get();
Department minDepartment = departmentList.stream()
		.min(Comparator.comparing(Department::getDepartSalary))
		.get();

4.4 collect

将当前的流转换为一个集合

4.4.1 Collectors.toList()

需求:获得一个保存所有部门名字的集合

List<String> departNameList = departmentList.stream()
		.map(Department::getDepartmentName)
		.collect(Collectors.toList());

4.4.2 Collectors.toSet()

需求:获得部门所有人的姓名(重名的忽略)

Set<String> personNameList = departmentList.stream()
		.flatMap(e -> e.getPersons().stream())
		.map(Person::getPersonName)
        .collect(Collectors.toSet());

4.4.3 Collectors.toMap(keyMapper, valueMapper)

需求:获得部门名称当做key,员工当做value的Map

Map<String, List<Person>> collect = departmentList.stream()
		.collect(Collectors.toMap(Department::getDepartmentName, Department::getPersons));

4.4.4 Collectors.joining()

需求:将所有的部门名字连起来,并且前缀是【,后缀是】,分隔符是,

departmentList.stream()
		.map(Department::getDepartmentName)
		.collect(Collectors.joining(",","[","]"));

4.4.5 Collectors.partitioningBy(predicate)

接受一个流,并将其分成两部分,使用 Predicate 对象判断一个元素应该属于哪个部分,并根据布尔值返回一个 Map 到列表。

需求:将一级部门和其他级别的部门分离出来

Map<Boolean, List<Department>> collect = departmentList
    .stream()
    .collect(Collectors.partitioningBy(e -> e.getDepartmenRank() == 1));

4.4.6 Collectors.groupingBy(classifier)

接受一个分类函数,用来对数据分组

需求:将部门按部门的阶级分组

Map<Integer, List<Department>> collect = departmentList
    .stream()
    .collect(Collectors.groupingBy(Department::getDepartmenRank));

4.4.7 组合收集器

各种收集器已经很强大了,但如果将它们组合起来,会变得更强大。

需求:统计各个阶级部门的平均薪资

Map<Integer, Double> collect = departmentList.stream()
.collect(Collectors.groupingBy(Department::getDepartmenRank,Collectors.averagingLong(Department::getDepartSalary)));

这个例子中用到了第二个收集器,用以收集最终结果的一个子集。这些收集器叫作下游收集器。收集器是生成最终结果的一剂配方,下游收集器则是生成部分结果的配方,主收集器中会用到下游收集器。这种组合使用收集器的方式,使得它们在 Stream 类库中的作用更加强大。

4.5 reduce

对流中的数据按照指定的方式计算出结果

需求:计算出部门总的日盈利

Long sum = departmentList.stream()
		.map(Department::getDepartProfit)
		.reduce(0L, (result, element) -> result + element);

4.6 anyMatch

可以用来判断是否有任意符合匹配条件的元素,结果为boolean类型。

需求:判断部门中有没有薪资大于9000的部门

boolean b = departmentList.stream()
                .anyMatch(e -> e.getDepartSalary() > 9000L);

4.7 allMatch

可以用来判断是否都符合匹配条件,结果为boolean类型。如果都符合结果为true,否则结果为false。

需求:判断部门薪资是不是都大于9000

boolean b = departmentList.stream()
                .allMatch(e -> e.getDepartSalary() > 9000L);

4.8 noneMatch

可以判断流中的元素是否都不符合匹配条件。如果都不符合结果为true,否则结果为false

需求:判断是不是所有的部门薪资都大于9000

boolean b = departmentList.stream()
                .noneMatch(e -> e.getDepartSalary() <= 9000L);

4.9 findAny

获取流中的任意一个元素。该方法没有办法保证获取的一定是流中的第一个元素。

需求:找到任意一个部门

Department department = departmentList
                .stream()
                .findAny()
                .get();

4.10 findFirst

获取流中的第一个元素。

需求:获得薪资最低的一个部门

Department department = departmentList
                .stream()
                .sorted(Comparator.comparing(Department::getDepartSalary))
                .findFirst()
                .get();

5.高级用法

5.1 对基本类型处理(mapToInt,mapToLong,mapToDouble)

在 Java 中,有一些相伴的类型,比如 int 和 Integer——前者是基本类型,后者是装箱类型。基本类型内建在语言和运行环境中,是基本的程序构建模块;而装箱类型属于普通的 Java 类,只不过是对基本类型的一种封装。由于装箱类型是对象,因此在内存中存在额外开销。比如,整型在内存中占用4 字节,整型对象却要占用 16 字节。这一情况在数组上更加严重,整型数组中的每个元素只占用基本类型的内存,而整型对象数组中,每个元素都是内存中的一个指针,指向 Java堆中的某个对象。在最坏的情况下,同样大小的数组,Integer[] 要比 int[] 多占用 6 倍内存。

将基本类型转换为装箱类型,称为装箱,反之则称为拆箱,两者都需要额外的计算开销。对于需要大量数值运算的算法来说,装箱和拆箱的计算开销,以及装箱类型占用的额外内存,会明显减缓程序的运行速度。

为了减小这些性能开销,Stream 类的某些方法对基本类型和装箱类型做了区分。高阶函数 mapToLong 和其他类似函数即为该方面的一个尝试。在 Java 8 中,仅对整型、长整型和双浮点型做了特殊处理,因为它们在数值计算中用得最多,特殊处理后的系统性能提升效果最明显。

如有可能,应尽可能多地使用对基本类型做过特殊处理的方法,进而改善性能。这些特殊的 Stream 还提供额外的方法,避免重复实现一些通用的方法,让代码更能体现出数值计算的意图。

需求:需要计算出公司部门利润的平均值,最大值,最小值,以及利润总和。

LongSummaryStatistics longSummaryStatistics = departmentList
    .stream()
    .mapToLong(Department::getDepartProfit)
    .summaryStatistics();
System.out.println("利润最大值"+longSummaryStatistics.getMax());
System.out.println("利润最小值"+longSummaryStatistics.getMin());
System.out.println("利润平均值"+longSummaryStatistics.getAverage());
System.out.println("利润总和"+longSummaryStatistics.getSum());

这些统计值在所有特殊处理的 Stream,如 DoubleStream、LongStream 中都可以得出。如无需全部的统计值,也可分别调用 min、max、average 或 sum 方法获得单个的统计值,同样,三种基本类型对应的特殊 Stream 也都包含这些方法。

6. 数据并行化

并 行 化 操 作 流 只 需 改 变 一 个 方 法 调 用。 如 果 已 经 有 一 个 Stream 对 象, 调 用 它 的parallel 方法就能让其拥有并行操作的能力。如果想从一个集合类创建一个流,调用parallelStream 就能立即获得一个拥有并行能力的流。

需求:并行的统计公司的所有的人员。

Set<Person> collect = departmentList.stream()
		.parallel()
		.flatMap(e -> e.getPersons().stream()).collect(Collectors.toSet());
Set<Person> collect1 = departmentList.parallelStream()
		.flatMap(e -> e.getPersons().stream()).collect(Collectors.toSet());

在底层,并行流还是沿用了 fork/join 框架。fork 递归式地分解问题,然后每段并行执行,最终由 join 合并结果,返回最后的值。文章来源地址https://www.toymoban.com/news/detail-607958.html

到了这里,关于Java8 函数式编程stream流的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java8 Stream流的合并

    最近的需求里有这样一个场景,要校验一个集合中每个对象的多个Id的有效性。比如一个Customer对象,有3个Id: id1 , id2 , id3 ,要把这些Id全部取出来,然后去数据库里查询它们是否存在。 通常情况下,我们都是从集合中取出对象的某一个字段,像这样: 现在要取3个字段,

    2024年02月02日
    浏览(52)
  • Java8中Stream详细用法大全

    Java 8 是一个非常成功的版本,这个版本新增的Stream,配合同版本出现的Lambda ,给我们操作集合(Collection)提供了极大的便利。Stream流是JDK8新增的成员,允许以声明性方式处理数据集合,可以把Stream流看作是遍历数据集合的一个高级迭代器。Stream 是 Java8 中处理集合的关键抽

    2023年04月08日
    浏览(77)
  • Java8的Stream流的学习

    Stream可以由数组或集合创建,对流的操作分为两种: 中间操作,每次返回一个新的流,可以有多个。 终端操作,每个流只能进行一次终端操作,终端操作结束后流无法再次使用。终端操作会产生一个新的集合或值。 stream和parallelStream的简单区分: stream是顺序流,由主线程按

    2024年02月07日
    浏览(41)
  • Java8 Stream 之groupingBy 分组讲解

    本文主要讲解:Java 8 Stream之Collectors.groupingBy()分组示例 Collectors.groupingBy() 分组之常见用法 功能代码: /**      * 使用java8 stream groupingBy操作,按城市分组list      */     public void groupingByCity() {         MapString, ListEmployee map = employees.stream().collect(Collectors.groupingBy(Employee::getCi

    2024年02月13日
    浏览(44)
  • Java8 Stream流处理树形结构数据

    参考资料 Java8新特性-使用Stream流递归实现遍历树形结构 ID为2,6,11的Menu 是 ID为1的Menu子节点 ID为3,4,5的Menu 是 ID为2的Menu子节点 💥 注意 是下面这种写法的一种更简单的写法

    2024年02月01日
    浏览(53)
  • Java8的stream之groupingBy()分组排序

    groupingBy()是Stream API中最强大的收集器Collector之一,提供与SQL的GROUP BY子句类似的功能。 需要指定一个属性才能使用,通过该属性执行分组。我们通过提供功能接口的实现来实现这一点 - 通常通过传递lambda表达式。 TreeMap默认按照key升序排序,collectPlan.descendingMap()可以进行降序排

    2024年02月12日
    浏览(38)
  • java8 Stream流常用方法(持续更新中...)

    操作对象 模拟数据 操作 打印结果 打印结果 注意:异常自己捕捉,就比如这里String转Intger就可能出现NumberFormatException异常 打印结果 打印结果 断点查看 打印结果 断点查看 持续更新中…

    2024年04月28日
    浏览(59)
  • Java8 Stream流常见操作--持续更新中

    Java8 Stream流是一种处理数据集合的方式,它可以对集合进行一系列的操作,包括过滤、映射、排序等,从而实现对集合中元素的处理和转换。Stream流是一种惰性求值的方式,只有在需要返回结果时才会执行实际操作,这种方式可以提高程序的性能。 延迟执行:Stream流中的操作

    2024年02月11日
    浏览(59)
  • Java8中Stream流求最大值最小值

    1. stream().reduce()实现 2. stream().max()或stream().min()实现 1. stream().reduce()实现 2. Collectors.summarizingInt()实现 3. stream().max()或stream().min()实现 1. stream().reduce()实现 2. Collectors.summarizingLong()实现 3. stream().max()或stream().min()实现 1. stream().reduce()实现 2. Collectors.summarizingLong()实现 3. stream().max(

    2024年01月17日
    浏览(36)
  • Java8-使用stream.sorted()对List排序

    1.流的定义 Stream 中文称为 “流”,通过将集合转换为这么一种叫做 “流” 的元素序列,通过声明性方式,能够对集合中的每个元素进行一系列并行或串行的操作! 如果流中的元素的类实现了 Comparable 接口,即有自己的排序规则,那么可以直接调用 sorted() 方法对元素进行排

    2024年02月16日
    浏览(54)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包