Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
接下来我将以代码片段的方式对Stream做较为详细的归纳,如需执行文章中讲解的代码,我们需要3个java文件,分别有用Employee雇员类定义、枚举状态定义和JUnit 测试执行类。
文件名为:
Employee.java
Status.java
TestStream.java
Employee.java内容如下:
package com.my.stream;
import java.util.Objects;
public class Employee {
private String name;
private Integer age;
private double salary;
private Status status;
public Employee(String name, Integer age, double salary, Status status) {
this.name = name;
this.age = age;
this.salary = salary;
this.status = status;
}
public Employee(String name, Integer age, double salary) {
this.name = name;
this.age = age;
this.salary = salary;
}
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Employee)) return false;
Employee employee = (Employee) o;
return age == employee.age &&
Double.compare(employee.salary, salary) == 0 &&
name.equals(employee.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age, salary);
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", age=" + age +
", salary=" + salary +
", status=" + status +
'}';
}
}
Status.java枚举文件内容如下:
package com.my.stream;
public enum Status {
FREE,
BUSY,
VOCATION,
ORTHER
}
TestStream.java文件则为本文重点,读者朋友可以从文档中的注解和代码片段了解到Stream流操作的若干知识点:
package com.my.stream;
import org.junit.Test;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/*
作用:对数据进行操作
条件:需要数据源,集合、数组中的数据
操作:流水线操作-过滤、筛选、切片、映射...
结果:对原有数据源不作改变,产生一个新的数据流
形式: 数据源(创建stream)->filter->map->...->终止操作(产生结果)
*/
public class TestStream {
//创建stream,4种方法
@Test
public void test1() {
//1、通过Collection系列集合提供的stream()-串行或parallelStream()-并行
List<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream();
//2、通过Arrays中的静态方法stream()获取,获得数组流
String[] arr = new String[10];
Stream<String> stream2 = Arrays.stream(arr);
//3、通过Stream类中的静态方法of(),获得数据流
Stream<String> stream3 = Stream.of("aa", "bb", "cc");
//4、创建无限流
//迭代
Stream<Integer> stream4 = Stream.iterate(0, (x) -> x + 2);
//stream4.limit(10).forEach(System.out::println);
//System.out.println("===");
//生成
Stream<Double> stream5 = Stream.generate(() -> Math.random());
//stream5.limit(10).forEach(System.out::println);
}
List<Employee> emps = Arrays.asList(
new Employee("张三", 28, 8000, Status.BUSY),
new Employee("李四", 25, 5000, Status.FREE),
new Employee("赵六", 33, 14000, Status.FREE),
new Employee("王五", 33, 9999, Status.FREE),
new Employee("赵六", 36, 12000, Status.VOCATION)
);
//中间操作(形成的流水线中间操作在终止之前不会执行,等到终止操作才会触发,惰性求值)
@Test
public void test2() {
//1、过滤-filter,接收lambda(断言型函数接口),从流中排除某些元素
Stream<Employee> s1 = emps.stream().filter((e) -> e.getAge() > 28);
s1.forEach(System.out::println);
System.out.println("\n===惰性求值==");
//验证 惰性求值
Stream<Employee> s2 = emps.stream()
.filter((e) -> {
System.out.println("这是中间操作");
return e.getAge() > 28;
});
s2.forEach(System.out::println);
System.out.println("\n===iterator==");
//由以上的输出可以看出,判断时执行了迭代操作,迭代操作由Stream内部完成,如果需要使用外部迭代则需要迭代器iterate
Iterator<Employee> it = emps.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
System.out.println("\n===limit==");
//2、截断流-limit(n),使新产生的元素输出不超过指定的数量(满足条件后,之后的迭代操作终止--短路操作提高效率)
emps.stream().limit(2).forEach(System.out::println);
System.out.println("\n===skip==");
//3、跳过元素-skip(n),返回一个扔掉前n个元素的流,若流中不足n个,则返回一个空流,与limit(n)互补
emps.stream().skip(2).forEach(System.out::println);
System.out.println("\n===distinct==");
//筛选-distinct,通过流所产生的元素的hashcode()和equals()去掉重复的元素
emps.stream().distinct().forEach(System.out::println); //Employee类需重写hashcode()和equals()方法,才能去掉重复
System.out.println("\n===map==");
//4、映射-map,接收lambda将元素转换为其他形式或提取信息,接收一个函数作为入参,该函数会被应用到每个元素上,并映射成一个新的元素
List<String> list = Arrays.asList("aa", "bb", "cc");
list.stream().map((str) -> str.toUpperCase()).forEach(System.out::println);
emps.stream().map((e) -> e.getName()).forEach(System.out::println);
System.out.println("\n===flatmap==");
//5、映射-flatmap,接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连成一个流
list.stream().flatMap(TestStream::filterCharacter).forEach(System.out::println);
//map和flatMap的区别
//如果使用list.stream().map(TestStream::filterCharacter) 流则变成{{a,a},{b,b},{c,c}}
//list.stream().flatMap(TestStream::filterCharacter)流则变成{a,a,b,b,c,c}
System.out.println("\n===sorted自然排序==");
//6、自然排序-sorted()
List<String> list2 = Arrays.asList("b", "a", "c");
list2.stream().sorted().forEach(System.out::println);
System.out.println("\n===sorted定制排序==");
//7、定制排序-sorted()
emps.stream().sorted((e1, e2) -> {
if (e1.getAge().equals(e2.getAge())) {
return e1.getName().compareTo(e2.getName());
} else {
return e1.getAge().compareTo(e2.getAge());
}
}).forEach(System.out::println);
}
public static Stream<Character> filterCharacter(String str) {
List<Character> list = new ArrayList<>();
for (Character ch : str.toCharArray()) {
list.add(ch);
}
return list.stream();
}
//终止操作
@Test
public void test3() {
//1、匹配-allMatch,检查所有元素是否匹配指定值
System.out.println("\n===allMatch==");
boolean b1 = emps.stream().allMatch((e) -> e.getStatus().equals(Status.FREE));
System.out.println(b1);
//2、匹配-anyMatch,是否至少匹配一个元素
System.out.println("\n===anyMatch==");
boolean b2 = emps.stream().anyMatch((e) -> e.getStatus().equals(Status.FREE));
System.out.println(b2);
//3、匹配-noneMatch,是否没有匹配所有元素
System.out.println("\n===noneMatch==");
boolean b3 = emps.stream().noneMatch((e) -> e.getStatus().equals(Status.ORTHER));
System.out.println(b3);
//4、返回第一个元素-findFirst(),返回Optional一个容器类,以避免数据为空的情况,使用get()获取返回的值
System.out.println("\n===findFirst==");
Optional<Employee> op1 = emps.stream().findFirst();
System.out.println(op1.get());
//orElse用于当查询内容为时,默认返回指定 对象数据
System.out.println("\n===orElse==");
Employee em1 = emps.stream().filter((e) -> {
return e.getStatus().equals(Status.ORTHER);
}).findFirst().orElse(new Employee("田七", 37, 9000, Status.ORTHER));
System.out.println(em1);
//5、返回流中任意一个元素-findAny,使用并行流parallelStream()演示效果,让每次拿到的数据不一样
System.out.println("\n===findAny==");
Optional<Employee> op2 = emps.parallelStream().filter((e) -> {
return e.getStatus().equals(Status.FREE);
}).findAny();
System.out.println(op2.get());
//6、返回数量-count(),返回流元素的数量
System.out.println("\n===count==");
Long count = emps.stream().count();
System.out.println(count);
//7、返回流中的最大值-max()
System.out.println("\n===max==");
Optional<Employee> op3 = emps.stream().max((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
System.out.println(op3.get());
//7、返回流中的最小值-min(),实例获取最低工资数值
System.out.println("\n===min==");
Optional<Double> op4 = emps.stream().map(Employee::getSalary).min(Double::compare);
System.out.println(op4.get());
//8、规约-reduce,将流中元素反复结合起来,得到一个新值
System.out.println("\n===reduce==");
List<Integer> list1 = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Integer sum = list1.stream().reduce(0, (x, y) -> x + y);
System.out.println(sum);
//reduce另一中方式,因为没有起始值,就可能会发回空,需要Optional类型接收
System.out.println("\n===reduce另一中方式==");
Optional<Double> op5 = emps.stream().map(Employee::getSalary).reduce(Double::sum);
System.out.println(op5.get());
//9、收集-collect,将流转为其他形式,接收一个Collector接口的实现,用于给Stream中元素做汇总
System.out.println("\n===collect==");
List<String> list2 = emps.stream().map(Employee::getName).collect(Collectors.toList());
System.out.println(list2);
//collect获取平均值
System.out.println("\n===collect获取平均值==");
double agv = emps.stream().collect(Collectors.averagingDouble(Employee::getSalary));
System.out.println(agv);
//collect获取工资总和
System.out.println("\n===collect获取工资总和==");
double salarySum = emps.stream().collect(Collectors.summingDouble(Employee::getSalary));
System.out.println(salarySum);
//collect分组
System.out.println("\n===collect分组==");
Map<Status, List<Employee>> map = emps.stream().collect(Collectors.groupingBy(Employee::getStatus));
System.out.println(map);
//collect连接字符串
System.out.println("\n===collect连接==");
String str = emps.stream().map(Employee::getName).collect(Collectors.joining(","));
System.out.println(str);
}
}