这个stream流和io流是完全不同的概念。
io流的工作是硬盘到内存以及内存到硬盘的操作,而stream流是针对集合的一种管道操作。
类似于mybatis-plus可以简化sql操作,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, "张三", "[email protected]", 30);
User user2 = new User(2, "李四", "[email protected]", 43);
User user3 = new User(3, "王五", "[email protected]", 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());
static <T> Stream<T> of(T... values)
它的作用是为给定元素创建顺序流,它传入的是可变长度参数,它返回的是一个Stream对象,可以带泛型。在上面这个例子中,返回的是Stream
<R> R collect(Supplier<R> 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());
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
这个方法是接口Collection中的一个方法,目的就是把List或Set集合对象转化为流,便于进行后续的流操作。这也是开发中最常见的创建流的方式。
其在Stream类中是这样定义的。
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
map()的作用就是将每个元素映射成新元素。观察方法参数,是一个Function函数式接口。
map()方法可以拿出对象中某一字段,也可以对基本类型集合每个数据进行转换操作或调用方法操作。
两个例子
List<String> list = Stream.of("Monkey", "Cat", "Tiger", "Lion")
.map(String::toUpperCase).collect(Collectors.toList());
System.out.println(list);
得到结果
[MONKEY, CAT, TIGER, LION]
相当于给list中每个String类型数据都调用了toUpperCase()方法,封装结果集为一个新集合。
List<Integer> list2 = Stream.of("Monkey", "Cat", "Tiger", "Lion")
.map(String::length).collect(Collectors.toList());
System.out.println(list2);
得到结果
[6, 3, 5, 4]
同理,相当于每个数据都调用了length()方法,封装结果为一个新集合。
Stream<T> peek(Consumer<? super T> action);
peek是一种特殊的map方法,其参数是一个消费型接口。
当方法没有返回值,或者参数就是返回值时,可以使用peek方法代替map。
如何处理二维数组与二维集合?我们使用flatMap方法进行处理。这里就先不细致讲解了,我们能弄明白map的用法即可。
IntStream mapToInt(ToIntFunction<? super T> mapper);
DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);
LongStream mapToLong(ToLongFunction<? super T> mapper);
使用这三个方法给流转化为数字流后就可以进行一些数字操作,例如max()、min()、sum()、average()、count() 等。
例如我们想计算用户年龄的平均值,可以这样操作。
OptionalDouble average = userList.stream().mapToInt(User::getAge).average();
OptionalDouble 有一个getAsDouble()方法,可以返回Double对象,我们就可以拿到它的数值了。
double average = userList.stream().mapToInt(User::getAge).average().getAsDouble();
关于Optional类,我们之后再做讲解。
再以上面最初的那个userList为例,如果我们想在某一字段加上过滤条件,例如要过滤出名字带五的人。传统做法需要使用for进行逐个遍历判断,并把符合条件的放到新集合中。
List<User> newUserList = new ArrayList<>();
for (User user : userList) {
if(!user.getName().contains("五")) {
newUserList.add(user);
}
}
如果我们用stream流
userList = userList.stream().filter(user -> !user.getName().contains("五")).collect(Collectors.toList());
一行代码即可解决,而且不用再去创建新集合。
Stream<T> filter(Predicate<? super T> predicate);
filter的方法定义是这样的,参数是一个断言型接口,实参可以使用lambda表达式或者方法引用。
这个过滤,是把符合断言为true的元素保留下来。
如果我们在前面不加任何过滤条件,这个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<Integer, String> map = userList.stream()
.filter(Objects::nonNull)
.collect(Collectors.toMap(User::getId, User::getName, (key1, key2) -> key2));
有一个成功就返回true
// true
boolean flag = userList.stream().anyMatch(user -> user.getAge() > 40);
所有都成功才返回true
// false
boolean flag = userList.stream().allMatch(user -> user.getAge() > 40);
所有都失败才返回true
// false
boolean flag = userList.stream().noneMatch(user -> user.getAge() > 40);