1 Stream概述

在Java 8 版本中新增的Stream,配合同版本出现的 Lambda ,给我们操作集合(Collection)提供了极大的便利。

那么什么是Stream?

Stream将要处理的元素集合看作一种流,在流的过程中,借助Stream API对流中的元素进行操作,比如:筛选、排序、聚合等。

Stream可以由数组或集合创建,对流的操作分为两种:

1、中间操作,每次返回一个新的流,可以有多个。
2、终端操作,每个流只能进行一次终端操作,终端操作结束后流无法再次使用。终端操作会产生一个新的集合或值。

另外,Stream有几个特性:

1、stream不存储数据,而是按照特定的规则对数据进行计算,一般会输出结果。
2、stream不会改变数据源,通常情况下会产生一个新的集合或一个值。
3、stream具有延迟执行特性,只有调用终端操作时,中间操作才会执行。

我自己总结就是可以间接的把Stream看做是MySQL的一些命令方法的 流化 的操作版。当使用熟练Stream之后会发现结合MySQL用起来真的超级爽。

2 Stream的创建

Stream可以通过集合数组创建。

1、通过 java.util.Collection.stream() 方法用集合创建流

List<Integer> list = Arrays.asList(1, 2, 3);
// 创建一个顺序流
Stream<Integer> stream = list.stream();
// 创建一个并行流
Stream<Integer> parallelStream = list.parallelStream();

2、使用java.util.Arrays.stream(T[] array)方法用数组创建流

int[] array={1,3,5,6,8};
IntStream stream = Arrays.stream(array);

3、使用Stream的静态方法:of()、iterate()、generate()(我不是很常用)

Stream<Integer> stream = Stream.of(1, 2, 3);
// of内部的方法,等同于使用数组创建流
@SafeVarargs
@SuppressWarnings("varargs") // Creating a stream from an array is safe
public static<T> Stream<T> of(T... values) {
    return Arrays.stream(values);
}


Stream<Integer> stream2 = Stream.iterate(0, (x) -> x + 3).limit(4);
stream2.forEach(System.out::print);
System.out.println();

Stream<Double> stream3 = Stream.generate(Math::random).limit(3);
stream3.forEach(System.out::println);

输出结果:

0369
0.4918954920054893
0.8246638264369555
0.17880449237798712

3 Stream的使用

创建一个案例类,之后的例子全部使用该类:

@Data
static class Dog {
    private String name;
    private int age;
    private String sex;

    public Dog() {
    }

    // 构造方法
    public Dog(String name, int age, String sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }
    
	public String toString() {
       return "name: " + name + "age: " + age + "sex: " + sex;
    }
}

利用上面的Dog类,创建一个集合,下面操作将使用该集合:

List<Dog> list = new ArrayList<>();
list.add(new Dog("zhangsan", 2, "male"));
list.add(new Dog("lisi", 1, "male"));
list.add(new Dog("zhanglong", 3, "female"));
list.add(new Dog("zhaowu", 6, "female"));
list.add(new Dog("wangda", 5, "male"));
list.add(new Dog("alisa", 8, "female"));

3.1 遍历/匹配(foreach/find/match)

Stream也是支持类似集合的遍历和匹配元素的,只是Stream中的元素是以Optional类型存在的。Stream的遍历、匹配非常简单。(Optional类是一个可以为null的容器对象。使用isPresent()方法,如果有值会返回true,调用get()方法会返回该对象。更详细说明请见:菜鸟教程Java 8 Optional类

public static void Test(String[] args){
    /* 使用上述集合 */
    
    // 遍历输出符合条件的元素
    list.stream().forEach(System.out::println);
    // 匹配第一个
    Optional<Dog> findFirst = list.stream().findFirst();
    // 匹配任意
    Optional<Dog> findAny = list.stream().findAny();
    // 是否包含符合特定条件的元素
    boolean anyMatch = list.stream().anyMatch(e -> e.getAge() == 2);
    System.out.println("匹配第一只狗:" + findFirst.get());
    System.out.println("匹配任意一个狗:" + findAny.get());
    System.out.println("是否存在年龄等于2的狗:" + anyMatch);
}

结果:

name: zhangsan age: 2 sex: male
name: lisi age: 1 sex: male
name: zhanglong age: 3 sex: female
name: zhaowu age: 6 sex: female
name: wangda age: 5 sex: male
name: alisa age: 8 sex: female

匹配第一只狗:name: zhangsan age: 2 sex: male
匹配任意一个狗:name: zhangsan age: 2 sex: male
是否存在年龄等于2的狗:true

注意:
使用findFirst()进行取第一个对象时在使用get()得到该对象时,有可能存在对象时null的情况,所以该处最好使用isPresent()进行一下非null校验。

3.2 过滤/筛选(filter)

筛选,就是按照一定的规则检查流中的元素,将符合条件规则的数据提取到新的流中
1、从Integer值的集合中筛选出大于8的值

public static void Test(String[] args){
    List<Integer> testList = Arrays.asList(1,4,2,6,4,9,3);
    testList.stream().filter(e -> e > 8).forEach(System.out::println);
}

结果:

9

2、将年龄大于7岁的Dog筛选出来

public static void Test(String[] args){
    /* 使用上述集合 */
    
    list.stream().filter(e -> e.getAge() > 7).forEach(System.out::println);
}

结果:

name: alisa age: 8 sex: female

3.3 映射(map/flatMap)

映射,可以将一个流的元素按照一定的映射规则映射到另一个流中。分为map和flatMap:

  • map:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
  • flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。

1、输出所有狗的名字

/* 上述集合 */

list.stream().map(Dog::getName).forEach(System.out::println);

结果:

zhangsan
lisi
zhanglong
zhaowu
wangda
alisa

2、将所有狗的年龄加2

public static void Test(String[] args){
    /* 使用上述集合 */
    
    list.stream().map(e -> {
            e.setAge(e.getAge() + 2);
            return e.getAge();
        }).forEach(System.out::println);
	/* 等同于 */
	list.stream().map(e -> e.getAge() + 2).forEach(System.out::println);
}

结果:

4
3
5
8
7
10

4
3
5
8
7
10

3、将两个字符集合组合并成一个新的字符集合。其中使用的collect(Collectors.toList())在后面会说。

public static void Test(String[] args){
    /* 使用上述集合 */
    
    List<String> list = Arrays.asList("a-b-c-d", "1-2-3-4");
    List<String> newList = list.stream().flatMap(e -> {
        // 将原集合中的单个元素拆分成一个数组
        String[] split = e.split("-");
        // 将数组中的元素转换成一个stream
        Stream<String> e2 = Arrays.stream(split);
        return e2;
    }).collect(Collectors.toList());

    System.out.println("处理前的集合:" + list);
    System.out.println("处理后的集合:" + newList);
}

结果:

处理前的集合:[a-b-c-d, 1-2-3-4]
处理后的集合:[a, b, c, d, 1, 2, 3, 4]

3.4 聚合(min/max/count)

max、min、count这些字眼你一定不陌生,在mysql中我们常用它们进行数据统计。Java Stream中也引入了这些概念和用法,方便了我们对集合、数组的数据统计工作。

1、取一个Integer值组成的集合中的最小值

public static void Test(String[] args){
    List<Integer> list = Arrays.asList(32,32,32,1,2,12,1,23,34,34,32);
    Optional<Integer> min = list.stream().min(Comparator.comparing(Integer::intValue));
    System.out.println(min.get());
}

结果:

1

2、取一个Integer值组成的集合中的最大值

public static void Test(String[] args){
    List<Integer> list = Arrays.asList(32,32,32,1,2,12,1,23,34,34,32);
    Optional<Integer> max = list.stream().max(Comparator.comparing(Integer::intValue));
    System.out.println(max.get());
}

结果:

34

3、取一组Dog中年龄最大的Dog

public static void Test(String[] args){
	/* 使用上述集合 */

    Optional<Dog> max = list.stream().max(Comparator.comparing(Dog::getAge));
    System.out.println(max.get());
}

结果:

name: alisa age: 8 sex: female

4、取一组Integer值组成的集合中值大于30的值的个数

public static void Test(String[] args){
	List<Integer> list = Arrays.asList(32,32,32,1,2,12,1,23,34,34,32);
    long max = list.stream().filter(e -> e > 30).count();
    System.out.println(max);
}

结果:

6

封装的Stream使用类,可以直接拿走使用:

public class StreamUtils {
    /**
     * 集合为空,创建空的Stream流;否则创建集合的Stream流
     * 避免出现空指针
     *
     * @param collection 集合
     * @param <T>   集合元素的泛型
     * @return Stream对象
     */
    private static <T> Stream<T> streamOf(Collection<T> collection) {
        return CollectionUtils.isEmpty(collection) ? Stream.empty() : collection.stream();
    }

    /**
     * 按照映射规则映射成一个新的集合流
     *
     * @param list  集合
     * @param mapper   集合属性元素
     * @param <T>   函数输入类型的泛型
     * @param <R>   函数结果类型的泛型
     * @return  新的集合
     */
    public static <T, R> List<R> mapList(List<T> list, Function<? super T, ? extends R> mapper) {
        return streamOf(list).map(mapper).collect(Collectors.toList());
    }

    /**
     * 根据给定的条件进行筛选,将符合条件的元素提取成新的流
     *
     * @param list          集合
     * @param predicate     筛选规则
     * @param <T>           流元素的类型
     * @return  符合条件的流集合
     */
    public static <T> List<T> filter(List<T> list, Predicate<? super T> predicate) {
        return streamOf(list).filter(predicate).collect(Collectors.toList());
    }

    /**
     * 根据给定的条件进行筛选,将符合条件的元素提取成新的流
     *
     * @param list          集合
     * @param predicates    多个筛选条件
     * @param <T>           流元素的类型
     * @return 符合条件的流集合
     */
    @SafeVarargs
    public static <T> List<T> filters(List<T> list, Predicate<? super T> ... predicates) {
        Stream<T> stream = streamOf(list);
        for (Predicate<? super T> predicate : predicates) {
            stream = stream.filter(predicate);
        }
        return stream.collect(Collectors.toList());
    }

    /**
     * 根据指定元素对集合进行升序排序
     *
     * @param list  集合
     * @param keyExtractor  用来排序的元素
     * @param <T>   函数输入类型的泛型
     * @param <U>   函数结果类型的泛型
     * @return  排序后的集合
     */
    public static <T, U extends Comparable<? super U>> List<T> sorted(
            List<T> list, Function<? super T, ? extends U> keyExtractor) {
        return streamOf(list).sorted(Comparator.comparing(keyExtractor)).collect(Collectors.toList());
    }

    /**
     * 根据指定元素对集合进行升序排序
     *
     * @param list  集合
     * @param keyExtractor  用来排序的元素
     * @param limit     排序后集合中保留的数量
     * @param <T>   函数输入类型的泛型
     * @param <U>   函数结果类型的泛型
     * @return  排序后的集合
     */
    public static <T, U extends Comparable<? super U>> List<T> sorted(
            List<T> list, Function<? super T, ? extends U> keyExtractor, Integer limit) {
        return streamOf(list).sorted(Comparator.comparing(keyExtractor)).limit(limit).collect(Collectors.toList());
    }

    /**
     * 根据指定元素对集合进行降序排序
     *
     * @param list  集合
     * @param keyExtractor  用来排序的元素
     * @param <T>   函数输入类型的泛型
     * @param <U>   函数结果类型的泛型
     * @return  排序后的集合
     */
    public static <T, U extends Comparable<? super U>> List<T> sortedDesc(
            List<T> list, Function<? super T, ? extends U> keyExtractor) {
        return streamOf(list).sorted(Comparator.comparing(keyExtractor).reversed()).collect(Collectors.toList());
    }

    /**
     *根据规则判断元素是否匹配
     *
     * @param list  集合
     * @param predicate 匹配规则
     * @param <T>   元素类型
     * @return  匹配结果
     */
    public static <T> boolean anyMatch(List<T> list, Predicate<? super T> predicate) {
        return streamOf(list).anyMatch(predicate);
    }

    /**
     * 将List集合转换成Map集合,同一个key时对value进行去重,保留第一个出现的value值
     *
     * @param list  集合
     * @param keyMapper     新的Map中的key
     * @param <T>   参数的类型
     * @return  转换后的Map集合   <key, T>
     */
    public static <T, K> Map< K, T> toMapDistinctFirst(List<T> list, Function<? super T, ? extends K> keyMapper) {
        return streamOf(list).collect(Collectors.toMap(keyMapper, Function.identity(), (key1, key2) -> key1));
    }

    /**
     * 将List集合转换成Map集合,同一个key时对value进行去重,保留最后出现的value值
     *
     * @param list  集合
     * @param keyMapper     新的Map中的key
     * @param <T>   参数的类型
     * @return  转换后的Map集合   <key, T>
     */
    public static <T, K> Map<K, T> toMapDistinctLast(List<T> list, Function<? super T, ? extends K> keyMapper) {
        return streamOf(list).collect(Collectors.toMap(keyMapper, Function.identity(), (key1, key2) -> key2));
    }

    /**
     * 将List转换为指定key->value键值对元素的Map集合
     * @param list  集合
     * @param keyMapper     Map的key元素
     * @param valueMapper   Map的value元素
     * @param <T>   函数输入的类型
     * @param <K>   函数输出的类型
     * @param <U>   函数输出的类型
     * @return  转换后的Map集合   <key, value>
     */
    public static <T, K, U> Map<K, U> toMap(List<T> list, Function<? super T, ? extends K> keyMapper,
                            Function<? super T, ? extends U> valueMapper) {
        return streamOf(list).collect(Collectors.toMap(keyMapper, valueMapper));
    }
}