一、Stream简介
Stream是Java8中处理集合的关键抽象概念,Stream API提供了一种高效且易于使用的处理数据的方式。你可以使用它对集合数据进行指定操作,如可以执行非常复杂的查找、过滤和映射数据等操作。
注意:
- Stream自己不会存储元素
- Stream不会改变源对象,Stream操作会返回一个新的Stream
- Stream操作是延迟执行的,这意味着等到获取结果时,Stream才会执行
Stream操作的三个步骤:
- Stream创建,由一个数据源,如集合、数组来获取一个Stream
- 中间操作。一个中间操作链,对数据源的数据进行处理
- 终止操作。执行中间操作链并产生结果。
Stream的操作步骤示例图:
二、Stream的创建
1.使用集合来创建Stream
Java8中的Collection接口被扩展,提供了两个获取Stream的方法:
//返回一个顺序流
default Stream<R> stream()
//返回一个并行流
default Stream<E> parallelStream()
2.使用数组创建Stream
Java8中的Arrays的静态方法stream()可以获取流
static <T> Stream<T> stream(T[] array)
static IntStream<T> stream(int[] array)
static LongStream<T> stream(long[] array)
static DoubleStream<T> stream(double[] array)
3.由值创建Stream
使用Stream的静态方法of来创建一个流,该方法可以接收任意数量的参数。
static<T> Stream<T> of(T... values)
4.由函数创建Stream
可以使用Stream的静态方法iterate()和generate()创建无限流
public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
public static<T> Stream<T> generate(Supplier<T> s)
Stream创建代码示例:
@Test
public void test01(){
//1.通过Collection系列集合提供的stream()或parallelStream()获取
ArrayList<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream();
//2.通过Arrays中的静态方法stream()获取
String[] strings=new String[10];
Stream<String> stream2 = Arrays.stream(strings);
//3.通过Stream类中的静态方法of()获取
Stream<String> stream3 = Stream.of(strings);
//4.创建无限流
//4.1迭代
Stream<Integer> stream4 = Stream.iterate(0, x-> x + 2);
//4.2生成
Stream<Double> stream5 = Stream.generate(() -> Math.random());
}
三、Stream的中间操作
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何处理,而是在种植操作时一次性全部处理,称为“惰性求值”。
1.筛选与切片
筛选与切片的相关方法介绍:
使用示例
创建一个Employee类用于后续使用:
public class Employee {
private Integer id;
private String name;
private Integer age;
private State state;
public Employee(Integer id, String name, Integer age, State state) {
this.id = id;
this.name = name;
this.age = age;
this.state = state;
}
@Override
public Employee(){
}
public Employee(Integer id) {
this.id = id;
}
public Employee(Integer id, Integer age) {
this.id = id;
this.age = age;
}
public Employee(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Employee employee = (Employee) o;
return Objects.equals(id, employee.id) &&
Objects.equals(name, employee.name) &&
Objects.equals(age, employee.age) &&
state == employee.state;
}
@Override
public int hashCode() {
return Objects.hash(id, name, age, state);
}
//省略getter、setter、toString方法
}
State枚举类的定义
public enum State {
BUSY,
FREE,
VACATION
}
filter方法——接收Lambda,从流中排除某些元素
public class TestStreamAPI02 {
List<Employee> employees=Arrays.asList(new Employee(1,"张三",60),
new Employee(2,"李四",50),
new Employee(3,"王五",45),
new Employee(26,"赵六",45),
new Employee(26,"赵六",45),
new Employee(26,"赵六",45));
//filter——接收Lambda,从流中排除某些元素
@Test
public void test01(){
//中间操作:不会执行任何操作
Stream<Employee> stream = employees.stream().filter((employee -> {
System.out.println("执行过滤操作");
return employee.getAge() > 45;
}));
//终止操作:一次性执行全部内容,即“惰性求值”
stream.forEach(System.out::println);
}
}
控制台输出结果:
limit——截断流,使其元素不超过给定数量
@Test
public void test02(){
employees.stream()
.filter(e->e.getAge()>18)
.limit(3)
.forEach(System.out::println);
}
控制台输出结果:
skip–跳过元素,返回一个扔掉了前n个元素的流,若流中的元素不足n个,则返回空流。与limit互补。
@Test
public void test03(){
employees.stream()
.skip(2)
.forEach(System.out::println);
}
控制台输出结果:
distinct–筛选,去取流中的hashcode()和equals相同的元素
@Test
public void test04(){
employees.stream()
.distinct()
.forEach(System.out::println);
}
Employee类中重写了hashcode()和equals()方法根据id, name, age, state属性进行比较计算对象是否相等,以此控制台输出结果:
2.映射
映射相关方法如下:
使用示例
map方法——接收Lambda,将元素转化成其他形式或提取信息。接收一个函数作为参数, 该函数会被应用到每个元素上,并将其映射成一个新的元素。
public class TestStreamAPI03 {
List<Employee> employees=Arrays.asList(new Employee(1,"张三",60),
new Employee(2,"李四",50),
new Employee(3,"王五",45),
new Employee(26,"赵六",45),
new Employee(26,"赵六",45),
new Employee(26,"赵六",45));
@Test
public void test01(){
List<String> list = Arrays.asList("a", "bb", "ccc", "dddd");
//将list中元素转化成其他形式
list.stream()
.map(s->s.toUpperCase())
.forEach(System.out::print);
//原集合不受到影响
list.forEach(System.out::print);
//提取list中元素的信息
employees.stream()
.map(employee -> employee.getName())
.forEach(System.out::print);
}
}
控制台输出结果:
flatMap方法–接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
public class TestStreamAPI03 {
List<Employee> employees=Arrays.asList(new Employee(1,"张三",60),
new Employee(2,"李四",50),
new Employee(3,"王五",45),
new Employee(26,"赵六",45),
new Employee(26,"赵六",45),
new Employee(26,"赵六",45));
//提取字符串中的每个字符到list中,并放入stream流
public static Stream<Character> filterCharacter(String str){
ArrayList<Character> list = new ArrayList<>();
for (Character ch:str.toCharArray()) {
list.add(ch);
}
return list.stream();
}
//测试flatMap
@Test
public void test02(){
List<String> list = Arrays.asList("a", "bb", "ccc", "dddd");
Stream<Stream<Character>> streams = list.stream()
.map(TestStreamAPI03::filterCharacter);//得到的流的形式类似{{a},{b,b},{c,c,c},{d,d,d,d}}
//不使用flatMap函数
System.out.println("不使用flatMap函数");
streams.forEach(stream->stream.forEach(System.out::print));
//使用flatMap函数
System.out.println("\n使用flatMap函数");
Stream<Character> streams2 = list.stream()
.flatMap(TestStreamAPI03::filterCharacter);//得到流的形式类似{a,b,b,c,c,c,d,d,d,d}
streams2.forEach(System.out::print);
}
}
控制台输出结果:
3.排序
排序的相关方法:
使用示例:
sorted()–产生一个新流,自然排序(Comparable)
public class TestStreamAPI04 {
List<Employee> employees=Arrays.asList(new Employee(1,"张三",60),
new Employee(2,"李四",50),
new Employee(3,"王五",45),
new Employee(23,"赵六",45),
new Employee(21,"田七",45),
new Employee(24,"天启",45));
@Test
public void test01(){
List<String> list = Arrays.asList("ddd", "ccc", "bbb", "aaa");
//默认使用泛型的实现Comparable接口中的compareTo方法进行比较
list.stream().sorted().forEach(System.out::println);
}
}
sorted(Comparator com)–产生一个新流,定制排序(Comparator)
@Test
public void test02(){
employees.stream().sorted((e1,e2)->{
if (e1.getAge().equals(e2.getAge())){
return e1.getId().compareTo(e2.getId());
}else{
return e1.getAge().compareTo(e2.getAge());
}
}).forEach(System.out::println);
}
四、Stream的终止操作
终端操作会从stream的流水线生成结果,其结果可以不是流的值,可以是List,Integer,甚至是Void。
1.查找与匹配
查找与匹配的相关方法:
测试 allMatch,anyMatch,noneMatch方法的使用
public class TestStreamAPI05 {
List<Employee> employees=Arrays.asList(new Employee(1,"张三",60),
new Employee(2,"李四",50, State.BUSY),
new Employee(3,"王五",45,State.BUSY),
new Employee(23,"赵六",45,State.FREE),
new Employee(21,"田七",40,State.VACATION),
new Employee(24,"天启",35,State.FREE));
//测试allMatch,anyMatch,noneMatch
@Test
public void test01(){
boolean allMatch = employees.stream().allMatch(e -> e.getAge() >= 45);
System.out.println(allMatch);
boolean anyMatch = employees.stream().anyMatch((e -> e.getAge() > 65));
System.out.println(anyMatch);
boolean noneMatch = employees.stream().noneMatch((e -> e.getAge() > 65));
System.out.println(noneMatch);
}
}
控制台输出结果:
测试findFirst,findAny方法的使用
//测试findFirst,findAny
@Test
public void test02(){
Optional<Employee> first = employees.stream()
.sorted((e1, e2) -> e1.getAge().compareTo(e2.getAge()))
.findFirst();
System.out.println(first.get());
Optional<Employee> any = employees.stream().filter(e -> e.getState()==State.FREE).findAny();
System.out.println(any.get());
}
控制台输出结果:
测试count,max,min方法的使用
//测试count,max,min
@Test
public void test03(){
//测试count
long count = employees.stream().count();
System.out.println(count);
//测试max
Optional<Employee> max = employees.stream().max((e1, e2) -> e1.getAge().compareTo(e2.getAge()));
System.out.println(max.get());
//测试min
Optional<Employee> min = employees.stream().min((e1, e2) -> e1.getAge().compareTo(e2.getAge()));
System.out.println(min.get());
}
控制台输出的结果:
2.规约
规约相关方法:
测试reduce(T identity,BinaryOperator)和reduce(BinaryOperator),其中identity为原始的结合值。
public class TestStreamAPI06 {
List<Employee> employees=Arrays.asList(new Employee(1,"张三",60),
new Employee(2,"李四",50, State.BUSY),
new Employee(3,"王五",45,State.BUSY),
new Employee(23,"赵六",45,State.FREE),
new Employee(21,"田七",40,State.VACATION),
new Employee(24,"天启",35,State.FREE));
@Test
public void test01(){
//计算整数数组累加
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Integer result = integers.stream().reduce(5, (x, y) -> x + y);
System.out.println(result);
//计算平均年龄
Optional<Integer> ageTotal = employees.stream().map(Employee::getAge).reduce(Integer::sum);
long empNum=employees.stream().count();
System.out.println(ageTotal.get()/empNum);
}
}
控制台输出结果:
3.收集
收集的方法定义
Collector接口中的方法的实现决定了如何对流执行收集操作(如收集到List、Set、Map)。Collectors实用类提供了很多静态方法,可以方便地创建常用的收集器实例,具体方法与实例如下图:
测试将流转换为其他形式,收集到新的集合上,包括Collectors.toList()、Collectors.toSet()、Collectors.toCollection(集合对象)
List<Employee> employees=Arrays.asList(new Employee(1,"张三",60,State.VACATION),
new Employee(2,"李四",50, State.BUSY),
new Employee(3,"王五",45,State.BUSY),
new Employee(23,"赵六",45,State.FREE),
new Employee(23,"赵六",45,State.FREE),
new Employee(21,"田七",40,State.VACATION),
new Employee(24,"天启",35,State.FREE));
@Test
public void test01(){
List<String> list = employees.stream().map(Employee::getName).collect(Collectors.toList());
list.forEach(System.out::println);
System.out.println("-----------------");
Set<String> set = employees.stream().map(Employee::getName).collect(Collectors.toSet());
set.forEach(System.out::println);
System.out.println("-----------------");
HashSet<String> hashSet = employees.stream().map(Employee::getName).collect(Collectors.toCollection(HashSet::new));
hashSet.forEach(System.out::println);
}
}
数组统计相关方法,包括counting、averagingDouble、summingInt等方法。
@Test
public void test02(){
//总数
Long sum= employees.stream().collect(Collectors.counting());
System.out.println(sum);
//平均值
Double averageAge = employees.stream().collect(Collectors.averagingDouble(Employee::getAge));
System.out.println(averageAge);
//某属性的综合
Integer ageSum = employees.stream().collect(Collectors.summingInt(Employee::getAge));
System.out.println(ageSum);
//最大值
Optional<Employee> maxAgeEmployee = employees.stream().collect(Collectors.maxBy((e1, e2) -> e1.getAge().compareTo(e2.getAge())));
System.out.println(maxAgeEmployee.get());
//最小值
Optional<Employee> minAgeEmployee = employees.stream().collect(Collectors.minBy((e1, e2) -> e1.getAge().compareTo(e2.getAge())));
System.out.println(minAgeEmployee.get());
//所有数据
IntSummaryStatistics totalData = employees.stream().collect(Collectors.summarizingInt(Employee::getAge));
System.out.println(totalData);
}
控制台输出结果:
测试分组groupingBy方法
@Test
public void test03(){
Map<State, List<Employee>> stateListMap = employees.stream().collect(Collectors.groupingBy(Employee::getState));
System.out.println(stateListMap);
}
程序输出结果:
{FREE=[Employee{id=23, name='赵六', age=45, state=FREE}, Employee{id=23, name='赵六', age=45, state=FREE}, Employee{id=24, name='天启', age=35, state=FREE}], VACATION=[Employee{id=1, name='张三', age=60, state=VACATION}, Employee{id=21, name='田七', age=40, state=VACATION}], BUSY=[Employee{id=2, name='李四', age=50, state=BUSY}, Employee{id=3, name='王五', age=45, state=BUSY}]}
测试多级分组:
@Test
public void test04(){
Map<State, Map<String, List<Employee>>> collect = employees.stream()
.collect(Collectors.groupingBy(Employee::getState, Collectors.groupingBy((e) -> {
if (((Employee) e).getAge() <= 30) {
return "青年";
} else if (((Employee) e).getAge() <= 50) {
return "中年";
} else {
return "老年";
}
})));
System.out.println(collect);
}
控制台输出结果:
{VACATION={老年=[Employee{id=1, name='张三', age=60, state=VACATION}], 中年=[Employee{id=21, name='田七', age=40, state=VACATION}]}, FREE={中年=[Employee{id=23, name='赵六', age=45, state=FREE}, Employee{id=23, name='赵六', age=45, state=FREE}, Employee{id=24, name='天启', age=35, state=FREE}]}, BUSY={中年=[Employee{id=2, name='李四', age=50, state=BUSY}, Employee{id=3, name='王五', age=45, state=BUSY}]}}
测试分区方法partitioningBy,接收Predicate,满足属于true,不满足就属于false。
//分区
@Test
public void test08(){
Map<Boolean, List<Employee>> collect = employees.stream().collect(Collectors.partitioningBy(employee -> employee.getAge() > 40));
System.out.println(collect);
}
程序输出结果:
{false=[Employee{id=21, name='田七', age=40, state=VACATION}, Employee{id=24, name='天启', age=35, state=FREE}], true=[Employee{id=1, name='张三', age=60, state=VACATION}, Employee{id=2, name='李四', age=50, state=BUSY}, Employee{id=3, name='王五', age=45, state=BUSY}, Employee{id=23, name='赵六', age=45, state=FREE}, Employee{id=23, name='赵六', age=45, state=FREE}]}
笔记总结自:https://www.bilibili.com/video/BV14W411u7Ly?from=search&seid=6050719224094931659