1.Stream流是什么?
- 这个stream流和io流是完全不同的概念。
io流的工作是硬盘到内存以及内存到硬盘的操作,而stream流是针对集合的一种管道操作。
类似于mybatis-plus可以简化sql操作,stream流也可以简化对集合的操作。 - Stream是一个流,在java.util.Stream包路径下
主要作用就是对集合数据进行查找过滤等操作,一种高效且易用的数据处理方式。
2. Stream流常用方法
方法 | 作用 |
---|---|
filter() | 接收lambda,从流中排除某些操作 |
limit() | 截断流,使其元素不超过给定对象 |
skip(n) | 跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流,与limit(n)互补 |
distinct | 筛选,通过流所生成元素的hashCode()和equals去除重复元素 |
map | 接受Lambda,将元素转换成其他形式或提取信息。接受一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素 |
sorted() | 自然排序(Comparable) |
sorted(Comparator com) | 定制排序(Comparator) |
allMatch | 检查是否匹配所有元素 |
anyMatch | 检查是否至少匹配一个元素 |
noneMatch | 检查是否没有匹配所有元素 |
findFirst | 返回第一个元素 |
findAny | 返回当前流中的任意元素 |
count | 返回流中元素的总个数 |
max | 返回流中最大值 |
min | 返回流中最小值 |
reduce | 归约操作可以将流中元素反复结合起来,得到一个值 |
collect | 将流转换为其他形式,接收一个Collector接口实现,用于给Stream中汇总的方法 |
用法
我们定义这样一个实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer id;
private String name;
private String email;
private Integer age;
}
//我们想创建三个User对象放到一个List中,先创建这三个user对象
User user1 = new User(1, "张三", "111@qq.com", 30);
User user2 = new User(2, "李四", "112@qq.com", 43);
User user3 = new User(3, "王五", "113@qq.com", 33);
//传统做法放到List中
List<User> userList = new ArrayList<>();
userList.add(user1);
userList.add(user2);
userList.add(user3);
//使用stream流
List<User> userList = Stream.of(user1, user2, user3).collect(Collectors.toList());
- of()
static Stream of(T… values)
它的作用是为给定元素创建顺序流,它传入的是可变长度参数,它返回的是一个Stream对象,可以带泛型。在上面这个例子中,返回的是Stream对象。 - collect()
R collect(Supplier supplier,
BiConsumer<R, ? super T> accumulator,
BiConsumer<R, R> combiner);
<R, A> R collect(Collector<? super T, A, R> collector);
它的作用是,Stream流操作完数据后,如果需要将流的数据进行保存,我们使用这个方法,将流中的数据保存到集合中,其常用的实参有这么三种,我们可以见名思意。
1.Collectors.toList()
2.Collectors.toSet()
3.Collectors.toMap()
我们看一下toList()方法的源码
public static <T>
Collector<T, ?, List<T>> toList() {
return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
(left, right) -> { left.addAll(right); return left; },
CH_ID);
}
取集合对象某一字段放到另一集合
将上面userList的name字段提取出来放到一个新的List集合中
传统做法
List <String> userNameList = new ArrayList<>();
for (User user : userList) {
userNameList.add(user.getName());
}
使用stream
List<String> userNameList = userList.stream().map(User::getName).collect(Collectors.toList());
- stream()
这个方法是接口Collection中的一个方法,目的就是把List或Set集合对象转化为流,便于进行后续的流操作。这也是开发中最常见的创建流的方式。
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
- filter()
Stream filter(Predicate<? super T> predicate);
filter的方法定义是这样的,参数是一个断言型接口,实参可以使用lambda表达式或者方法引用。
这个过滤,是把符合断言为true的元素保留下来。 - count()
如果我们在前面不加任何过滤条件,这个count()毫无用处,因为它获得的是集合内所有元素个数总和,我们用集合的 size() 方法即可获得。
但我们加了条件后,就可以知道满足条件的元素有多少。
long count = userList.stream().filter(user -> !user.getName().contains(“五”)).count();
这样就知道名字里不带“五”的用户有几个了。
还是上面这个userList,如果我想把这个List转换成Map,key是user的id,value是user的name,使用传统方式的话,需要遍历整个List集合,拿到元素再放入Map。
我们使用上文提到的 Collectors.toMap() 处理这个。不过我们需要先注意两个问题
Map的key重复问题
我们通过key重复,value覆盖的方式解决。
空指针异常,userList可能有null的存在。
我们通过filter过滤的方式解决。
Map<Integer, String> map = userList.stream()
.filter(Objects::nonNull)
.collect(Collectors.toMap(User::getId, User::getName, (key1, key2) -> key2)); - anyMatch()
有一个成功就返回true
// true
boolean flag = userList.stream().anyMatch(user -> user.getAge() > 40); - allMatch()
所有都成功才返回true// false
boolean flag = userList.stream().allMatch(user -> user.getAge() > 40); - noneMatch()
所有都失败才返回true
// false
boolean flag = userList.stream().noneMatch(user -> user.getAge() > 40);