Java 8 Stream API学习总结

Java 8 API添加了一个新的笼统称为流Stream,能够让你以一种声明的办法处理数据。Stream API能够极大进步Java程序员的生产力,让程序员写出高效率、洁净、简练的代码。这种风格即将处理的元素调集看作一种流, 流在管道中传输, 并且能够在管道的节点上进行处理, 比方挑选, 排序,聚合等。元素流在管道中经过中心操作(intermediate operation)的处理,最后由终究操作(terminal operation)得到前面处理的成果。

这一次为什么要系统性的总结一下 Java 8 Stream API 呢?说得简略点,咱们先不管功能,咱们便是为了 装x ,并且要让这个 x 装得再优异一些,仅此而已!

Java 8 Stream API学习总结

Stream基础知识

流程

创立流流的中心操作流的终究操作

创立流

咱们需要把哪些元素放入流中,常见的api有:

// 运用List创立流
list.stream()
// 运用一个或多个元素创立流
Stream.of(T value)
Stream.of(T... values)
// 运用数组创立流
Arrays.stream(T[] array)
// 创立一个空流
Stream.empty()
// 两个流兼并
Stream.concat(Stream<? extends T> a, Stream<? extends T> b)
// 无序无限流
Stream.generate(Supplier<T> s)
// 经过迭代发生无限流
Stream.iterate(final T seed, final UnaryOperator<T> f)

流的中心操作

// 元素过滤
filter
limit
skip
distinct
// 映射
map
flatmap
// 排序

流的终究操作

经过流对元素的终究操作,咱们想得到一个什么样的成果

结构测验数据

职工实体类

/**
* 职工实体类
* @author Erwin Feng
* @since 2020/4/27 2:10
*/
public class Employee {
/** 职工ID */
private Integer id;
/** 职工名字 */
private String name;
/** 职工薪资 */
private Double salary;
/** 结构办法、getter and setter、toString */
}

测验数据列表

[
{
"id":1,
"name":"Jacob",
"salary":1000
},
{
"id":2,
"name":"Sophia",
"salary":2000
},
{
"id":3,
"name":"Rose",
"salary":3000
},
{
"id":4,
"name":"Lily",
"salary":4000
},
{
"id":5,
"name":"Daisy",
"salary":5000
},
{
"id":6,
"name":"Jane",
"salary":5000
},
{
"id":7,
"name":"Jasmine",
"salary":6000
},
{
"id":8,
"name":"Jack",
"salary":6000
},
{
"id":9,
"name":"Poppy",
"salary":7000
}
]

Stream API Test

filter 过滤

需求:查找薪酬为5000的职工列表

List<Employee> employees = list.stream().filter(employee -> employee.getSalary() == 5000)
.peek(System.out::println)
.collect(Collectors.toList());
Assert.assertEquals(2, employees.size());

map 映射

需求:将薪酬大于5000的职工放到Leader目标中

List<Leader> leaders = list.stream().filter(employee -> employee.getSalary() > 5000).map(employee -> {
Leader leader = new Leader();
leader.setName(employee.getName());
leader.setSalary(employee.getSalary());
return leader;
}).peek(System.out::println).collect(Collectors.toList());
Assert.assertEquals(3, leaders.size());

flatMap 水平映射

需求:将多维的列表转化为单维的列表

阐明:

咱们将薪酬在1000-3000的分为一个列表,4000-5000分为一个列表,6000-7000分为一个列表。

将这三个列表组合在一起构成一个多维列表。

List<Employee> employees = multidimensionalList.stream().flatMap(Collection::stream).collect(Collectors.toList());
Assert.assertEquals(9, employees.size());

sorted 排序

需求:依据薪酬排序

// 薪酬从小到大排序
List<Employee> employees = list.stream().sorted(Comparator.comparing(Employee::getSalary)).peek(System.out::println).collect(Collectors.toList());
// 薪酬从大到小排序
List<Employee> employees2 = list.stream().sorted(Comparator.comparing(Employee::getSalary).reversed()).peek(System.out::println).collect(Collectors.toList());

min 最小值

double minValue = list.stream().mapToDouble(Employee::getSalary).min().orElse(0);
Assert.assertEquals(1000, minValue, 0.0);
Employee employee = list.stream().min(Comparator.comparing(Employee::getSalary)).orElse(null);
assert employee != null;
Assert.assertEquals(employee.getSalary(), minValue, 0.0);

max 最大值

double maxValue = list.stream().mapToDouble(Employee::getSalary).max().orElse(0);
Assert.assertEquals(7000, maxValue, 0.0);

average 平均值

double sum = list.stream().mapToDouble(Employee::getSalary).sum();
double averageValue = list.stream().mapToDouble(Employee::getSalary).average().orElse(0);
Assert.assertEquals(sum / list.size(), averageValue, 0.0);

match 匹配

// allMatch 调集中的元素都要满意条件才会回来true
// 薪酬都是大于等于1000的
boolean isAllMatch = list.stream().allMatch(employee -> employee.getSalary() >= 1000);
Assert.assertTrue(isAllMatch);
// anyMatch 调集中只需有一个元素满意条件就会回来true
// 有没有薪酬大于等于7000
boolean isAnyMatch = list.stream().anyMatch(employee -> employee.getSalary() >= 7000);
Assert.assertTrue(isAnyMatch);
// noneMatch 调集中没有元素满意条件才会回来true
// 没有薪酬小于1000的
boolean isNoneMatch = list.stream().noneMatch(employee -> employee.getSalary() < 1000);
Assert.assertTrue(isNoneMatch);

distinct 去重

默许的 distinct() 不接纳参数,是依据 Object#equals(Object) 去重。依据API介绍,这是一个有中心状态的操作。

List<Employee> employees = list.stream().distinct().collect(Collectors.toList());
Assert.assertEquals(9, employees.size());

假如咱们要依据目标中的某个特点去重的,能够运用 StreamEx

// 运用StreamEx去重
List<Employee> employees2 = StreamEx.of(list).distinct(Employee::getSalary).collect(Collectors.toList());
Assert.assertEquals(7, employees2.size());

当然也能够运用JDK Stream API

private static <T>Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
Map<Object, Boolean> result = new ConcurrentHashMap<>();
return t -> result.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}
List<Employee> employees3 = list.stream().filter(distinctByKey(Employee::getSalary)).collect(Collectors.toList());
Assert.assertEquals(7, employees3.size());

reduce 削减

需求:核算薪酬总和

// 先将职工列表转换为薪酬列表
// 再核算薪酬总和
double salarySum = list.stream().map(Employee::getSalary).reduce(Double::sum).orElse(0.0);
double sum = list.stream().mapToDouble(Employee::getSalary).sum();
Assert.assertEquals(salarySum, sum, 0.0);

别的,咱们也能够设定一个累加函数的标识值

double salarySum5 = list.stream().map(Employee::getSalary).reduce(1.00, Double::sum);
Assert.assertEquals(salarySum5, sum + 1, 0.0);

collector 流的停止成果

// joining 拼接字符串
String employeeNames = list.stream().map(Employee::getName).collect(Collectors.joining(", "));
System.out.println(employeeNames); // Jacob, Sophia, Rose, Lily, Daisy, Jane, Jasmine, Jack, Poppy
// 回来一个List
List<String> employeeNameList = list.stream().map(Employee::getName).collect(Collectors.toList());
System.out.println(employeeNameList);
// 回来一个Set
Set<String> employeeNameSet = list.stream().map(Employee::getName).collect(Collectors.toSet());
System.out.println(employeeNameSet);
// 回来一个Vector
Vector<String> employeeNameVector = list.stream().map(Employee::getName).collect(Collectors.toCollection(Vector::new));
System.out.println(employeeNameVector);
// 回来一个Map
Map<Integer, String> employeesMap = list.stream().collect(Collectors.toMap(Employee::getId, Employee::getName));
System.out.println(employeesMap);

count 核算

需求:薪酬为5000的职工数

不运用流

int count2 = 0;
for (Employee employee : list) {
if (employee.getSalary() == 5000) {
count2++;
}
}
System.out.println(count2);

运用流

long count3 = list.stream().filter(employee -> employee.getSalary() == 5000).count();
Assert.assertEquals(count3, count2);

summarizingDouble 核算分析

DoubleSummaryStatistics employeeSalaryStatistics = list.stream().collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println("employee salary statistics:" + employeeSalaryStatistics);
DoubleSummaryStatistics employeeSalaryStatistics2 = list.stream().mapToDouble(Employee::getSalary).summaryStatistics();
System.out.println("employee salary statistics2:" + employeeSalaryStatistics2);

{count=9, sum=39000.000000, min=1000.000000, average=4333.333333, max=7000.000000}

partitioningBy 分区

分红满意条件(true)和不满意条件(false)两个区

需求:找出薪酬大于5000的职工

Map<Boolean, List<Employee>> map = list.stream().collect(Collectors.partitioningBy(employee -> employee.getSalary() > 5000));
System.out.println("true:" + map.get(Boolean.TRUE));
System.out.println("false:" + map.get(Boolean.FALSE));

true:[Employee{id=7, name='Jasmine', salary=6000.0}, Employee{id=8, name='Jack', salary=6000.0}, Employee{id=9, name='Poppy', salary=7000.0}]

false:[Employee{id=1, name='Jacob', salary=1000.0}, Employee{id=2, name='Sophia', salary=2000.0}, Employee{id=3, name='Rose', salary=3000.0}, Employee{id=4, name='Lily', salary=4000.0}, Employee{id=5, name='Daisy', salary=5000.0}, Employee{id=6, name='Jane', salary=5000.0}]

groupingBy 分组

需求:依据职工薪酬分组

Map<Double, List<Employee>> map = list.stream().collect(Collectors.groupingBy(Employee::getSalary));
System.out.println(map);

再举一个比如:薪酬 一> 总和(薪酬*职工数)

Map<Double, Double> map3 = list.stream().collect(Collectors.groupingBy(Employee::getSalary, Collectors.summingDouble(Employee::getSalary)));
System.out.println(map3);

parallel 平行核算

简略的说,便是发动多个线程核算

private static void cal(Employee employee) {
try {
long sleepTime = employee.getSalary().longValue();
TimeUnit.MILLISECONDS.sleep(sleepTime);
logger.info("employee name: {}", employee.getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
list.stream().parallel().forEach(StreamTest::cal);
2020-05-15 01:47:14.231 [ForkJoinPool.commonPool-worker-4] INFO  com.fengwenyi.study_stream.StreamTest - employee name: Jacob
2020-05-15 01:47:15.226 [ForkJoinPool.commonPool-worker-2] INFO  com.fengwenyi.study_stream.StreamTest - employee name: Sophia
2020-05-15 01:47:16.226 [ForkJoinPool.commonPool-worker-1] INFO  com.fengwenyi.study_stream.StreamTest - employee name: Rose
2020-05-15 01:47:17.226 [ForkJoinPool.commonPool-worker-3] INFO  com.fengwenyi.study_stream.StreamTest - employee name: Lily
2020-05-15 01:47:18.225 [main] INFO  com.fengwenyi.study_stream.StreamTest - employee name: Jane
2020-05-15 01:47:18.228 [ForkJoinPool.commonPool-worker-7] INFO  com.fengwenyi.study_stream.StreamTest - employee name: Daisy
2020-05-15 01:47:19.226 [ForkJoinPool.commonPool-worker-5] INFO  com.fengwenyi.study_stream.StreamTest - employee name: Jack
2020-05-15 01:47:19.228 [ForkJoinPool.commonPool-worker-6] INFO  com.fengwenyi.study_stream.StreamTest - employee name: Jasmine
2020-05-15 01:47:21.234 [ForkJoinPool.commonPool-worker-4] INFO  com.fengwenyi.study_stream.StreamTest - employee name: Poppy

file 文件操作

try (PrintWriter printWriter = new PrintWriter(Files.newBufferedWriter(Paths.get(tempFilePath)))) { // 运用 try 主动封闭流
list.forEach(printWriter::println);
list.forEach(employee -> printWriter.println(employee.getName())); // 将职工的名字写到文件中
}
// 从文件中读取职工的名字
List<String> s = Files.lines(Paths.get(tempFilePath)).peek(System.out::println).collect(Collectors.toList());

测验代码

Study Java 8 Stream API

Java 8 Stream API学习总结

学习链接

  • noodlespan > Stream系列
  • Java 8 中的 Streams API 详解
  • Java8新特性-Stream API 常用完整版
  • Stream In Java