Stream流
Stream流
3.1 概述
Stream是Java8 API的新成员,它允许以声明性方式处理数据集合 。
特点:
- 代码简洁:函数式编程写出的代码简洁且意图明确,使用stream接口让你从此告别for循环。
- 多核友好:Java函数式编程使得编写并行程序从未如此简单,你需要的全部就是调用一下方法。
3.2准备阶段
book.java(书籍类)
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author 尹稳健~
* @version 1.0
* @time 2022/8/25
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Book {
private Long id;
private String name;
private String category;
private Integer score;
private String intro;
}
author.java (作者类)
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* @author 尹稳健~
* @version 1.0
* @time 2022/8/25
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Author {
private Long id;
private String name;
private Integer age;
private String intro;
private List<Book> books;
}
生成简单数据的方法
private static List<Author> getAuthors(){
Author author1 = new Author(1L, "蒙多", 33, "一个从菜刀中明悟哲理的祖安人", null);
Author author2 = new Author(2L, "亚索", 15, "狂风也追逐不上他的思考速度", null);
Author author3 = new Author(3L, "易", 14, "是这个世界限制了他的思维", null);
Author author4 = new Author(4L, "易", 14, "是这个世界限制了他的思维", null);
List<Book> book1 = new ArrayList<>();
List<Book> book2 = new ArrayList<>();
List<Book> book3 = new ArrayList<>();
book1.add(new Book(1L,"刀的两侧是光明与黑暗","哲学,爱情",88,"用一把刀划分爱恨"));
book1.add(new Book(2L,"一个人不能死在同一把刀下","个人成长,爱情",99,"讲述如何从失败到成功"));
book2.add(new Book(3L,"那风吹不到的地方","哲学",85,"带你用思维去领略世界的尽头"));
book2.add(new Book(3L,"那风吹不到的地方","哲学",85,"带你用思维去领略世界的尽头"));
book2.add(new Book(4L,"吹或不吹","个人传记,爱情",56,"一个哲学家的恋爱观注定很难把他"));
book3.add(new Book(5L,"你的剑就是我的剑","爱情",56,"无法想象一个武者能对他的伴侣"));
book3.add(new Book(6L,"风与剑","个人传记",100,"两个哲学家的灵魂和肉体的碰撞会激起怎么样的"));
book3.add(new Book(6L,"风与剑","个人传记",100,"两个哲学家的灵魂和肉体的碰撞会激起怎么样的"));
author1.setBooks(book1);
author2.setBooks(book2);
author3.setBooks(book3);
author4.setBooks(book3);
ArrayList<Author> authorList = new ArrayList<>(Arrays.asList(author1,author2,author3,author4));
return authorList;
}
3.3快速入门
3.3.1 需求
从getAuthors方法获取到的集合,进行筛选过滤,获取所有年龄小于18岁的作者,去重
3.3.2实现
使用普通方法实现:
List<Author> authors = getAuthors();
ArrayList<Author> authors_lt = new ArrayList<>(); // 新的数组保存作者
for (Author author : authors) {
if (author.getAge()<18){
authors_lt.add(author);
}
}
Set<Author> set = new HashSet<>(); // 使用set去重
for (Author author : authors_lt) {
set.add(author);
}
System.out.println(set);
}
使用Stream流的方式实现:
考虑到可能会看不懂,所以匿名内部类也留在这:
List<Author> authors = getAuthors();
// 获取所有年龄小于18岁的作者,去重
authors.stream() // 将集合转为流
.distinct() // 去重
.filter(new Predicate<Author>() {
@Override
public boolean test(Author author) {
return author.getAge() < 18;
}
}) // 过滤条件 Predicate
.forEach(new Consumer<Author>() {
@Override
public void accept(Author author) {
System.out.println(author);
}
}); //遍历打印 Consumer
简化:
List<Author> authors = getAuthors();
// 获取所有年龄小于18岁的作者,去重
authors.stream() // 将集合转为流
.distinct() // 去重
.filter(author -> author.getAge() < 18) // 过滤条件 Predicate
.forEach(author -> System.out.println(author)); //遍历打印 Consumer
idea自带的debug:
点开后不要急,他在计算中
过了一会就有了
去重
过滤:
3.4常用操作
3.4.1创建流
单列集合(Collection、List):集合对象.stream()
List<Author> authors = getAuthors();
Stream<Author> stream = authors.stream();
数组:Arrays.stream(数组)或者使用Stream.of来创建
Integer[] arr = {1,2,3,4};
Stream<Integer> stream = Arrays.stream(arr);
Stream<Integer> stream = Stream.of(arr);
双列集合:转换为单列集合后在创建
Stream<Map.Entry<String, Object>> stream = map.entrySet().stream();
3.4.2中间操作
filter
可以对流中的数据进行过滤,符合条件的留在流中
例如:打印所有姓名长度大于1的作者的名字
List<Author> authors = getAuthors();
// 打印所有姓名长度大于1的作者的名字
authors.stream() // 将数据放入stream流中
.filter(author -> {return author.getName().length()>1;}) // 过滤姓名长度大于1的作者的名字
.forEach(author -> System.out.println(author.getName())); // 终结操作
map
可以对流进行计算或者转换
例如:打印所有作家的名字
List<Author> authors = getAuthors();
// 打印所有作家的名字
authors.stream()
.map(new Function<Author, String>() {
@Override
public String apply(Author author) {
return author.getName();
}
}) // 取出所有的名字
.forEach(s -> System.out.println(s)); // 进行打印
使用简化的lambda表达式:
List<Author> authors = getAuthors();
// 打印所有作家的名字
authors.stream()
.map(author -> author.getName()) // 取出所有的名字
.forEach(s -> System.out.println(s)); // 进行打印
distinct
去除流中重复的数据
例如:打印所有作家的名字,要求其中不能有重复的数据
List<Author> authors = getAuthors();
// 打印所有作家的名字,要求其中不能有重复的数据
authors.stream()
.distinct() // 去重
.forEach(author -> System.out.println(author.getName())); // 遍历打印
注意:distinct方法是以来Obejct的equals方法来判断是否是相同的对象。所以需要重写equals方法
sorted
可以对流中的数据进行排序
例如:对流中的数据进行年龄降序排序,且不能有相同重复的数据
List<Author> authors = getAuthors();
authors.stream()
.distinct()
.sorted(new Comparator<Author>() {
@Override
public int compare(Author o1, Author o2) {
return o2.getAge() - o1.getAge(); // o1 - o2 升序 , o2 - o1 降序
}
})
.forEach(author -> System.out.println(author));
使用lambda表达式:
List<Author> authors = getAuthors();
authors.stream()
.distinct()
.sorted((o1, o2) -> o2.getAge() - o1.getAge())
.forEach(author -> System.out.println(author));
注意:如果调用空参的sorted方法需要实现Comparator接口,重写方法
limit
可以设置流的最大长度,超过的部分被抛弃
例如:对流中的数据按照年龄降序排序,且不能有重复的数据,然后打印其中年龄最大的2个作家的姓名
List<Author> authors = getAuthors();
authors.stream()
.distinct()
.sorted((o1, o2) -> o2.getAge() - o1.getAge())
.limit(2)
.forEach(author -> System.out.println(author));
skip
跳过流中的前n个数据,返回剩下的数据
例如:打印除了年龄最大的作家外的所有作家,不能有重复的数据,且按照年龄的降序排序
List<Author> authors = getAuthors();
authors.stream()
.distinct()
.sorted((o1, o2) -> o2.getAge() - o1.getAge())
.skip(1)
.forEach(author -> System.out.println(author));
flatMap
map只能把一个对象转换成另一个对象来作为流中的数据,而flatMap可以把一个对象转化为多个对象作为流中的数据
例一:打印所有书籍的名称,要求重复数据去重
// 打印所有书籍的名称,要求重复数据去重
authors.stream()
.distinct()
.flatMap(new Function<Author, Stream<Book>>() {
@Override
public Stream<Book> apply(Author author) {
return author.getBooks().stream().distinct();
}
})
.forEach(new Consumer<Book>() {
@Override
public void accept(Book book) {
System.out.println(book);
}
});
使用简化的lambda表达式:
// 打印所有书籍的名称,要求重复数据去重
authors.stream()
.distinct()
.flatMap(author -> author.getBooks().stream())
.distinct()
.forEach(book -> System.out.println(book));
例二:打印数据的所有分类,按分类去重,不能出现这种格式:哲学,爱情
List<Author> authors = getAuthors();
authors.stream()
.flatMap(author -> author.getBooks().stream())
.distinct()
.flatMap(book -> Arrays.asList(book.getCategory().split(",")).stream())
.distinct()
.forEach(s -> System.out.println(s));
3.4.3终结操作
forEach
对流中的数据进行遍历操作,我们通过传入的参数去指定对遍历到的数据进行具体操作
例子:列出所有作家的名字
List<Author> authors = getAuthors();
authors.stream()
.distinct()
.forEach(author -> System.out.println(author.getName()));
count
可以用来获取当前流中数据的个数
例子:打印这些作家的所出书籍的数量,去重
List<Author> authors = getAuthors();
authors.stream()
.distinct()
.map(author -> author.getBooks())
.distinct()
.count();
max&min
可以用来获取流中的最值
例子:分别获取作家书籍中评分的最高和最低值
List<Author> authors = getAuthors();
Optional<Integer> max = authors.stream()
.flatMap(author -> author.getBooks().stream())
.distinct()
.map(book -> book.getScore())
.max((o1, o2) -> o1 - o2);
System.out.println(max);
Optional<Integer> min = authors.stream()
.flatMap(author -> author.getBooks().stream())
.distinct()
.map(book -> book.getScore())
.min((o1, o2) -> o1 - o2);
System.out.println(min);
collect
把当前流转换为一个集合
例一:获取一个存放所有作者姓名的List集合,注意去重
List<Author> authors = getAuthors();
// 例一:获取一个存放所有作者姓名的List集合,注意去重
List<String> list = authors.stream()
.map(author -> author.getName())
.distinct()
.collect(Collectors.toList());
System.out.println(list);
例二:获取一个全是书名的集合,去重
List<Author> authors = getAuthors();
// 例二:获取一个全是书名的集合,去重
Set<String> set = authors.stream()
.flatMap(author -> author.getBooks().stream())
.map(book -> book.getName())
.collect(Collectors.toSet());
System.out.println(set);
例三:获取一个map集合,map的key为作者名,value为List
List<Author> authors = getAuthors();
// 例三:获取一个map集合,map的key为作者名,value为List<Book>
Map<String, List<Book>> map = authors.stream()
.distinct()
.collect(Collectors.toMap(author -> author.getName(), author -> author.getBooks()));
System.out.println(map);
注意collect()参数是传入一个Collectors工具类
查找与匹配
anyMatch
可以用来判断是否任意数据符合匹配条件的数据,结果为Boolean类型
例子:判断是否有年龄在25岁以上的
List<Author> authors = getAuthors();
boolean b = authors.stream()
.anyMatch(author -> author.getAge() > 25);
System.out.println(b);
allMatch
可以用来判断是否任意数据符合匹配条件的数据,结果为Boolean类型,全符合时为true,有一条数据为false那就是false
例子:判断是否所有作家都是成年人?
List<Author> authors = getAuthors();
boolean b1 = authors.stream()
.allMatch(author -> author.getAge() > 17);
System.out.println(b1);
noneMatch
可以用来判断是否任意数据不符合匹配条件的数据,结果为Boolean类型,全不符合为true,否则false
例子:判断作家是否都没有超过100岁
List<Author> authors = getAuthors();
boolean b2 = authors.stream()
.noneMatch(author -> author.getAge() > 100);
System.out.println(b2);
findAny
获取流中任意个一条数据,该方法没办法保证获取的一定是流的第一条数据
例子:获取任意个大于18的作家,如果存在就输出他的名字
List<Author> authors = getAuthors();
Optional<Author> any = authors.stream()
.filter(author -> author.getAge() > 18)
.findAny();
System.out.println(any);
findFirst
获取流中的第一个条数据
例子:获取年龄最小的作家,并输出他的名字
List<Author> authors = getAuthors();
Optional<Author> first = authors.stream()
.sorted((o1, o2) -> o1.getAge() - o2.getAge())
.findFirst();
System.out.println(first);
reduce归并
对流中的数据按照你指定的计算方式计算出一个结果
reduce的作用是把stream中的数据组合起来,我们可以传入一个初始值,它会按照我们的计算方式依次拿到流中的数据和初始化值进行运算,计算结果再和每次遍历的数据计算。
Optional reduce(BinaryOperator accumulator);底层实现:
boolean foundAny = false;
T result = null;
for (T element : this stream) {
if (!foundAny) {
foundAny = true;
result = element;
}
else
result = accumulator.apply(result, element);
}
return foundAny ? Optional.of(result) : Optional.empty();
T reduce(T identity, BinaryOperator accumulator);底层实现:
T result = identity;
for (T element : this stream)
result = accumulator.apply(result, element)
return result;
U reduce(U identity,
BiFunction<U, ? super T, U> accumulator,
BinaryOperator combiner);底层源码
U result = identity;
for (T element : this stream)
result = accumulator.apply(result, element)
return result;
例一:使用reduce求所有作者的年龄和
List<Author> authors = getAuthors();
// 使用reduce求所有作者的年龄和
Integer reduce = authors.stream()
.map(author -> author.getAge())
.reduce(0, (integer, integer2) -> integer + integer2);
System.out.println(reduce);
例二:使用reduce求所有作家年龄最大的
List<Author> authors = getAuthors();
// 使用reduce求所有作家年龄最大的
Integer max = authors.stream()
.map(author -> author.getAge())
.reduce(Integer.MIN_VALUE, (result, element) -> result > element ? result : element);
System.out.println(max);
例三:使用reduce求所有作者的最小值
List<Author> authors = getAuthors();
// 使用reduce求所有作家年龄最小的
Integer min = authors.stream()
.map(author -> author.getAge())
.reduce(Integer.MAX_VALUE, (result, element) -> result < element ? result : element);
System.out.println(min);
第二种方式:
Optional<Integer> min1 = authors.stream()
.map(author -> author.getAge())
.reduce((integer, integer2) -> integer < integer2 ? integer : integer2);
System.out.println(min1);
3.5注意事项:
- 惰性求值(如果没有终结操作,那么他的中间操作都不会执行)
- 流是一次性的(一旦这个流使用了终结操作,那么这个流就不会在被使用)
- 不会影响原数据(我们可以对流进行很多处理,但是正常情况下是不会影响到原来的数据)