Java8新特性,Stream入门详解,丰富案例等你细品

开发 前端
Stream流是JDK8新增用来处理集合、数组、文件等数据,借助Lambda表达式,极大提高编程效率和程序可读性,同时拥有串行和并行两种数据处理模式,并行模式可以充分利用多核CPU性能,通过 fork/join 方式拆解任务加速处理。

JDK8的重要更新除去Lambda之外还有Stream,两者结合使用为操作和计算数据提供了极大的便利。

本篇文章并不打算长篇大论,文章过长会阅读疲劳,Stream也并不是一两篇文章可以介绍清楚的,本篇主要介绍Stream的简单理论加上案例来体会一下Stream的作用,便利和魅力,后续文章会系统讲解Stream体系!

Stream是什么

Stream就是【流】的意思,与 java.io包中的输入流,输出流是两个不同的概念

Stream流是JDK8新增用来处理集合、数组、文件等数据,借助Lambda表达式,极大提高编程效率和程序可读性,同时拥有串行和并行两种数据处理模式,并行模式可以充分利用多核CPU性能,通过 fork/join 方式拆解任务加速处理。

Stream好处

  • 函数式编程:让Java原本臃肿的代码变的简洁,这当然是需要配合Lambda实现
  • 高效的并行处理机制,比之前的for循环加if...else,挨个元素处理速度要快上许多
  • 具有多种数据的处理实现,比如筛选,去重,转换,查询,遍历等内置操作

Stream特点

  • 流与集合、数组、文件不同,不是数据结构,不存储数据,目的是处理数据,将处理结果返回或者转换
  • 流在计算数据时,如果需要使用到集合中元素,会取出使用,并不修改源数据,流只使用数据一次
  • 支持延迟计算,只有等到执行终止操作时才会执行计算,可以降低不必要的CPU资源浪费

Stream操作分类

  • 创建流:可以通过集合、数组、IO资源、Stream的构造函数创建
  • 中间操作:对数据的计算操作,比如筛选,去重,转换等操作,一个中间操作返回一个新的Stream,来支持连续计算
  • 终止操作:每个流只能有一次终止操作,终止之后流无法使用,会产生一个计算结果,可以根据需求转换为想要的结果类型

概念先不说那么多,免得云里雾里,接下来我们直接通过案例来使用Stream对集合的操作

案例:

  • 获取运费大于5000元的运单,并放到新集合中
  • 将推荐运单按照运费从高到低排序
  • 统计最高运费,最低运费,平均运费
  • 将运单按照运费从高到低排序,相同者按照距离从高到低排序
  • 将运单按货物类型分类,将运单按货物类型和目的地分类,将运单按照运费是否高于5000元分为两部分

接下来通过传统方式和Stream两种分别实现,对比不同

提前准备:

import java.math.BigDecimal;

public class Waybill {
// id
private Long id;
// 运单编号
private String wayNo;
// 运费
private BigDecimal price;
// 货物类型
private String freightType;
// 距离
private BigDecimal distance;
// 目的地
private String endAddress;

public Waybill(Long id, String wayNo, BigDecimal price, String freightType, BigDecimal distance, String endAddress) {
this.id = id;
this.wayNo = wayNo;
this.price = price;
this.freightType = freightType;
this.distance = distance;
this.endAddress = endAddress;
}

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getWayNo() {
return wayNo;
}

public void setWayNo(String wayNo) {
this.wayNo = wayNo;
}

public BigDecimal getPrice() {
return price;
}

public void setPrice(BigDecimal price) {
this.price = price;
}

public String getFreightType() {
return freightType;
}

public void setFreightType(String freightType) {
this.freightType = freightType;
}

public BigDecimal getDistance() {
return distance;
}

public void setDistance(BigDecimal distance) {
this.distance = distance;
}

public String getEndAddress() {
return endAddress;
}

public void setEndAddress(String endAddress) {
this.endAddress = endAddress;
}

@Override
public String toString() {
return "Waybill{" +
"id=" + id +
", wayNo='" + wayNo + '\'' +
", price=" + price +
", freightType='" + freightType + '\'' +
", distance=" + distance +
", endAddress='" + endAddress + '\'' +
'}';
}
}

1、获取运费大于5000元的运单,并放到新集合中

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class WaybillMain {
private static List<Waybill> waybills = new ArrayList<>();

static {
// 创建数据
waybills.add(new Waybill(1L,"Y11111111111",new BigDecimal(1000),"钢材",new BigDecimal(200),"上海市"));
waybills.add(new Waybill(2L,"Y22222222222",new BigDecimal(2000),"钢材",new BigDecimal(300),"郑州市"));
waybills.add(new Waybill(3L,"Y33333333333",new BigDecimal(3000),"水泥",new BigDecimal(300),"北京市"));
waybills.add(new Waybill(4L,"Y44444444444",new BigDecimal(4000),"水泥",new BigDecimal(400),"广州市"));
waybills.add(new Waybill(5L,"Y55555555555",new BigDecimal(5000),"沙子",new BigDecimal(500),"上海市"));
waybills.add(new Waybill(6L,"Y66666666666",new BigDecimal(6000),"板材",new BigDecimal(500),"深圳市"));
waybills.add(new Waybill(7L,"Y77777777777",new BigDecimal(7000),"蔬菜",new BigDecimal(500),"杭州市"));
}

public static void main(String[] args){
// 1、传统写法
// 获取运费大于5000元的运单,并放到新集合中
List<Waybill> newWaybills = new ArrayList<Waybill>();
for (Waybill waybill : waybills) {
// 判断价格大于5000,BigDecimal需要使用compareTo方法比较
// 1:左边比右边大,0:相等,-1:右边比左边大
if(waybill.getPrice().compareTo(new BigDecimal(5000)) == 1) {
newWaybills.add(waybill);
}
}
// 遍历
for (Waybill newWaybill : newWaybills) {
System.out.println(newWaybill);
}
// 2、Stream + Lambda写法
System.out.println("**华丽丽的分割线**");
// 1) 通过集合的stream()方法创建流对象
Stream<Waybill> stream = waybills.stream();
// 2) 通过流对象的方法计算数据,filter:过滤数据
// filter接收一个过滤条件,item为当前操作的元素,比较价格是否大于5000,满足条件的过滤出来,放到一个新的Stream对象中
Stream<Waybill> waybillStream = stream.filter(item -> item.getPrice().compareTo(new BigDecimal(5000)) == 1);
// 3) 将过滤后的stream转换为新的集合,调用collect方法即可,toList()转换为List集合,toSet转换为Set集合
List<Waybill> collect = waybillStream.collect(Collectors.toList());
// 遍历,通过方法引用遍历
collect.forEach(System.out::println);
}
}

运行结果:

解释:

  • filter():方法就是中间操作,意为过滤符合条件的数据,但是这个数据你还不使用,就先不执行
  • collect():方法是终结操作,意为要将Stream的计算结果转换为一个List集合,Stream认为你要用计算结果了,所以会执行计算,之后保存结果到新的集合中
  • 计算过程Stream中是不存储数据的,没有获取数据的方法

2、将推荐运单按照运费从高到低排序

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class WaybillMain {

private static List<Waybill> waybills = new ArrayList<>();

static {
// 创建数据
waybills.add(new Waybill(1L,"Y11111111111",new BigDecimal(1000),"钢材",new BigDecimal(200),"上海市"));
waybills.add(new Waybill(2L,"Y22222222222",new BigDecimal(2000),"钢材",new BigDecimal(300),"郑州市"));
waybills.add(new Waybill(3L,"Y33333333333",new BigDecimal(3000),"水泥",new BigDecimal(300),"北京市"));
waybills.add(new Waybill(4L,"Y44444444444",new BigDecimal(4000),"水泥",new BigDecimal(400),"广州市"));
waybills.add(new Waybill(5L,"Y55555555555",new BigDecimal(5000),"沙子",new BigDecimal(500),"上海市"));
waybills.add(new Waybill(6L,"Y66666666666",new BigDecimal(6000),"板材",new BigDecimal(500),"深圳市"));
waybills.add(new Waybill(7L,"Y77777777777",new BigDecimal(7000),"蔬菜",new BigDecimal(500),"杭州市"));
}

public static void main(String[] args) {
// 1、升序排序
List<Waybill> collect1 = waybills.stream().sorted(Comparator.comparing(Waybill::getPrice)).collect(Collectors.toList());
System.out.println("**升序排序**");
collect1.forEach(System.out::println);
// 2、降序排序,调用reversed方法即可降序
List<Waybill> collect2 = waybills.stream().sorted(Comparator.comparing(Waybill::getPrice).reversed()).collect(Collectors.toList());
System.out.println("**降序排序**");
collect2.forEach(System.out::println);

// 3、如果只想获取运单号
List<String> collect3 = waybills.stream().sorted(Comparator.comparing(Waybill::getPrice)).map(Waybill::getWayNo).collect(Collectors.toList());
System.out.println("**降序只获取运单号**");
collect3.forEach(System.out::println);
// 4、先按距离,再按运费,通过thenComparing方法做继续排序
List<Waybill> collect4 = waybills.stream().sorted(Comparator.comparing(Waybill::getDistance).thenComparing(Waybill::getPrice)).collect(Collectors.toList());
System.out.println("**先按距离再按运费**");
collect4.forEach(System.out::println);

// 5、自定义排序
List<Waybill> collect5 = waybills.stream().sorted((o1, o2) -> {
// 排序规则:根据货物类型排序,相同的根据距离排序
if (o1.getFreightType().equals(o2.getFreightType())) {
return o1.getDistance().compareTo(o2.getDistance());
} else {
return o1.getFreightType().compareTo(o2.getFreightType());
}
}).collect(Collectors.toList());
System.out.println("**自定义排序: 根据货物类型排序,相同的根据距离排序**");
collect5.forEach(System.out::println);
}
}

运行结果:

3、统计最高运费,最低运费,平均运费

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class WaybillMain {

private static List<Waybill> waybills = new ArrayList<>();

static {
// 创建数据
waybills.add(new Waybill(1L,"Y11111111111",new BigDecimal(1000),"钢材",new BigDecimal(200),"上海市"));
waybills.add(new Waybill(2L,"Y22222222222",new BigDecimal(2000),"钢材",new BigDecimal(300),"郑州市"));
waybills.add(new Waybill(3L,"Y33333333333",new BigDecimal(3000),"水泥",new BigDecimal(300),"北京市"));
waybills.add(new Waybill(4L,"Y44444444444",new BigDecimal(4000),"水泥",new BigDecimal(400),"广州市"));
waybills.add(new Waybill(5L,"Y55555555555",new BigDecimal(5000),"沙子",new BigDecimal(500),"上海市"));
waybills.add(new Waybill(6L,"Y66666666666",new BigDecimal(6000),"板材",new BigDecimal(500),"深圳市"));
waybills.add(new Waybill(7L,"Y77777777777",new BigDecimal(7000),"蔬菜",new BigDecimal(500),"杭州市"));
}

public static void main(String[] args){
// 1、最高运费,通过max方法
Optional<Waybill> max = waybills.stream().max(Comparator.comparing(Waybill::getPrice));
System.out.println("**运费最高**");
System.out.println(max);
// 2、最低运费,通过min方法
Optional<Waybill> min = waybills.stream().min(Comparator.comparing(Waybill::getPrice));
System.out.println("**运费最低**");
System.out.println(min);
// 3、平均运费,通过 Collectors.averagingDouble计算平均值,需要将 BigDecimal转换为double类型
Double avg = waybills.stream().collect(Collectors.averagingDouble(item -> item.getPrice().doubleValue()));
System.out.println("**平均运费**");
System.out.println(avg);
}

}

运行截图:

4、将运单按货物类型分类,将运单按货物类型和目的地分类,将运单按照运费是否高于5000元分为两部分

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class WaybillMain {

private static List<Waybill> waybills = new ArrayList<>();

static {
// 创建数据
waybills.add(new Waybill(1L,"Y11111111111",new BigDecimal(1000),"钢材",new BigDecimal(200),"上海市"));
waybills.add(new Waybill(2L,"Y22222222222",new BigDecimal(2000),"钢材",new BigDecimal(300),"郑州市"));
waybills.add(new Waybill(3L,"Y33333333333",new BigDecimal(3000),"水泥",new BigDecimal(300),"北京市"));
waybills.add(new Waybill(4L,"Y44444444444",new BigDecimal(4000),"水泥",new BigDecimal(400),"广州市"));
waybills.add(new Waybill(5L,"Y55555555555",new BigDecimal(5000),"沙子",new BigDecimal(500),"上海市"));
waybills.add(new Waybill(6L,"Y66666666666",new BigDecimal(6000),"板材",new BigDecimal(500),"深圳市"));
waybills.add(new Waybill(7L,"Y77777777777",new BigDecimal(7000),"蔬菜",new BigDecimal(500),"杭州市"));
}

public static void main(String[] args) {
// 1、运单按货物类型分类
Map<String, List<Waybill>> collect1 = waybills.stream().collect(Collectors.groupingBy(Waybill::getFreightType));
System.out.println("** 运单按货物类型分类 **");
collect1.forEach((key,value) -> {
System.out.println("key==>" + key + ",value==>" + value);
});

// 2、运单按货物类型和目的地分类
Map<String, Map<String, List<Waybill>>> collect2 = waybills.stream().collect(Collectors.groupingBy(Waybill::getFreightType, Collectors.groupingBy(Waybill::getEndAddress)));
System.out.println("** 运单按货物类型和目的地分类 **");
collect2.forEach((key,value) -> {
System.out.println("key==>" + key + ",value==>" + value);
});

// 3、运单按照运费是否高于5000元分为两部分,这个叫分区了
Map<Boolean, List<Waybill>> collect3 = waybills.stream().collect(Collectors.partitioningBy(item -> item.getPrice().compareTo(new BigDecimal(5000)) == 1));
System.out.println("** 运单按照5000分区 **");
collect3.forEach((key,value) -> {
System.out.println("key==>" + key + ",value==>" + value);
});
}

}

运行截图:

总结

  • Stream的概念、好处、特点和操作分类
  • Stream操作集合案例,体会便利之处
  • 案例自己写几遍,再观察自己的项目中哪里可以使用Stream替换
责任编辑:武晓燕 来源: 今日头条
相关推荐

2022-12-09 07:48:10

Java8Stream表达式

2015-08-28 09:43:49

Java 8新特性处理集合

2021-03-02 07:13:54

Java8版本升级

2021-03-04 08:14:37

Java8开发接口

2014-04-16 07:43:31

Java 8JRE

2023-05-12 07:40:01

Java8API工具

2021-02-22 11:51:15

Java开发代码

2023-07-19 08:03:05

Future异步JDK

2014-07-15 14:12:17

Java8

2023-03-15 17:37:26

Java8ListMap

2014-07-15 14:48:26

Java8

2023-07-03 08:20:35

MySQL窗口函数

2020-05-25 16:25:17

Java8Stream函数式接口

2014-04-15 09:40:04

Java8stream

2014-07-16 16:42:41

Java8streamreduce

2012-06-12 11:07:56

笔记本评测

2014-10-20 13:57:59

JavaFX 8Java 8

2021-05-06 20:03:00

JavaStream代码

2021-10-18 09:38:02

JavaJava8编程语言

2017-10-25 11:05:14

Java
点赞
收藏

51CTO技术栈公众号