JDK8特性:Stream流

1. Stream流是什么?

这个stream流和io流是完全不同的概念。
io流的工作是硬盘到内存以及内存到硬盘的操作,而stream流是针对集合的一种管道操作。
类似于mybatis-plus可以简化sql操作,stream流也可以简化对集合的操作。

2. 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());

2.1 of()

static <T> Stream<T> of(T... values) 

它的作用是为给定元素创建顺序流,它传入的是可变长度参数,它返回的是一个Stream对象,可以带泛型。在上面这个例子中,返回的是Stream对象。

2.2 collect()

<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());

2.3 stream()

default Stream<E> stream() {
        return StreamSupport.stream(spliterator(), false);
}

这个方法是接口Collection中的一个方法,目的就是把List或Set集合对象转化为流,便于进行后续的流操作。这也是开发中最常见的创建流的方式。

2.4 map()

其在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()方法,封装结果为一个新集合。

2.4.1 peek()

Stream<T> peek(Consumer<? super T> action);

peek是一种特殊的map方法,其参数是一个消费型接口。
当方法没有返回值,或者参数就是返回值时,可以使用peek方法代替map。

2.4.2 flatMap()

如何处理二维数组与二维集合?我们使用flatMap方法进行处理。这里就先不细致讲解了,我们能弄明白map的用法即可。

2.4.3 其余

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());

一行代码即可解决,而且不用再去创建新集合。

2.5 filter()

Stream<T> filter(Predicate<? super T> predicate);

filter的方法定义是这样的,参数是一个断言型接口,实参可以使用lambda表达式或者方法引用。
这个过滤,是把符合断言为true的元素保留下来

2.6 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() 处理这个。不过我们需要先注意两个问题

  1. Map的key重复问题
    我们通过key重复,value覆盖的方式解决。
  2. 空指针异常,userList可能有null的存在。
    我们通过filter过滤的方式解决。
Map<Integer, String> map = userList.stream()
.filter(Objects::nonNull)
.collect(Collectors.toMap(User::getId, User::getName, (key1, key2) -> key2));

2.7 判断方法

2.7.1 anyMatch()

有一个成功就返回true

//  true
boolean flag = userList.stream().anyMatch(user -> user.getAge() > 40);

2.7.2 allMatch()

所有都成功才返回true

// false
boolean flag = userList.stream().allMatch(user -> user.getAge() > 40);

2.7.3 noneMatch()

所有都失败才返回true

// false
boolean flag = userList.stream().noneMatch(user -> user.getAge() > 40);

你可能感兴趣的:(java,开发语言)