概述 

Java 8引入的Stream API用于对集合(如列表、集合等)进行函数式操作(如过滤、映射、规约等)。这类流提供了一种高效且易读的方式来处理集合中的数据。 

获取Stream对象的方式有哪些个?

1 Stream接口提供的: of静态方法

public static<T> Stream<T> of(T... values) { // 传入一些T类型数据;
    return Arrays.stream(values);
}
public static<T> Stream<T> ofNullable(T t) {
    return t == null ? Stream.empty()
                     : StreamSupport.stream(new Streams.StreamBuilderImpl<>(t), false);
}
public static<T> Stream<T> of(T t) {
    return StreamSupport.stream(new Streams.StreamBuilderImpl<>(t), false);
}

2 集合当中stream()方法

List/Set

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

3 数组工具类Arrays.stream(T[] t)

public static <T> Stream<T> stream(T[] array) {
    return stream(array, 0, array.length);
}

4 基本使用

大体分为两大类方法:

  • 中间方法「返回值本身还是一个Stream对象,支持链式调用」

  • 终结方法,它不支持链式调用的.

  1. 集合的操作.通过以下方法获取Stream对象;

default Stream<E> stream() {
    return StreamSupport.stream(spliterator(), false);
}
package com.tingyi.demo04;
​
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
​
/**
 * @author 听忆
 * Stream流基本应用:
 */
public class Test01 {
    public static void main(String[] args) {
        // 搞一个集合.对集合元素进行操作;
        List<String> nameList = new ArrayList<>();
        nameList.add("张三");
        nameList.add("张三");
        nameList.add("张三");
        nameList.add("张三");
        nameList.add("张三");
        nameList.add("李四");
        nameList.add("王五");
        nameList.add("赵六");
        nameList.add("田七");
        nameList.add("孙八");
​
        // ①. 获取Stream对象;
        Stream<String> stream = nameList.stream();
        // 获取一下集合当中名称是姓张的.
        // 过滤集合元素.
        // Predicate<? super T> predicate = s -> {
        //     // 判断集合元素的逻辑写在这里.返回是一个boolean值;
        //     if(s.contains("张")){
        //         return true;
        //     }
        //     return false;
        // };
        // 应用了lambda表达式来简化操作
        // 本质: 匿名内部类对象.
        // 本质: 匿名内部类对象.  --> 真理.
        List<String> list = stream.filter(s -> s.contains("张三"))
                .collect(Collectors.toList());
        System.out.println(list);
​
    }
}
​

一个重点:

  • 当一个接口当作方法的参数的时候,实参传入的就是: 实现了这个接口的实现类的对象

  • 当一个接口当作了方法的返回值,那么返回的一定是: 实现了这个接口的实现类的对象

方法名称描述方法类型
filter针对于每个元素进行判断,符合条件的留下,不符合过滤掉了.中间方法
map把一个数据类型转换为别外一个数据类型.中间方法
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
package com.tingyi.demo04;
​
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
​
/**
 * @author 听忆
 */
public class Test02 {
    public static void main(String[] args) {
        List<String> nameList = new ArrayList<>();
        nameList.add("张三");
        nameList.add("张三");
        nameList.add("张三");
        nameList.add("张三");
        nameList.add("张三");
        nameList.add("李四");
        nameList.add("王五");
        nameList.add("赵六");
        nameList.add("田七");
        nameList.add("孙八");
​
        // 想在这个集合当中,给所有的元素都添加一个字符串,"好好学习, 天天向上!"
        // 张三-好好学习, 天天向上;
        Stream<String> stream = nameList.stream(); // Stream对象.
        List<String> newList = stream.map(s -> s + "-好好学习, 天天向上!")
                .collect(Collectors.toList());
        System.out.println(newList);
​
        // Stream特性: 一次性的对象.用完之后,不能重复使用.如果你还要用,自己搞对象;
        // Stream特性: 一次性的对象.用完之后,不能重复使用.如果你还要用,自己搞对象;
        // Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
        List<String> list1 = nameList.stream().filter(s -> s.contains("李四")).collect(Collectors.toList());
        System.out.println(list1);
​
​
         System.out.println("--------------------------------");
        // // 一步到位写法.
         List<String> list = nameList.stream()
                 .map(s -> s + "hello world!")
                 .collect(Collectors.toList());
         System.out.println(list);
​
    }
}
 

①. Stream流,只能消费一次.不能重复使用;

​ 如果重复使用则会抛出异常:

​ Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed

②. Function, t -> r, 这里使用的泛型,实际上咱们可以是同一个类型相互之间有转换;

5 将Stream还原成List/数组对象

Stream当中提供了方法:

<R, A> R collect(Collector<? super T, A, R> collector);// 参数: 实现好Collector接口的实现类的对象;
public interface Collector<T, A, R> {}

如果要自动手动实现,复杂度太高了,而且我们进行方法调用,必须得手写一个实现类。显然,这么做不太现实.jdk当中提供了工具类. Collectors

static class CollectorImpl<T, A, R> implements Collector<T, A, R> {
    private final Supplier<A> supplier;
    private final BiConsumer<A, T> accumulator;
    private final BinaryOperator<A> combiner;
    private final Function<A, R> finisher;
    private final Set<Characteristics> characteristics;
// 实现类,是在Collectors当中的一个静态内部类.
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);
}

上边的方法,它是定义在Collectors工具类当中的.还是一个静态的方法.方便我们直接使用类名调用.

Stream流当中,collect(Collector),表示收集方法,就是将一个stream对象转换为其它集合姿势;所以我们在使用的时候,都会借助工具类.造成别自己去写实现类.

// Supplier, 生产型接口.这里我们生产啥类型的集合,就可以将Stream转换成啥类型的集合;
public static Collector toCollection(Supplier collectionFactory) {
    return new CollectorImpl<>(collectionFactory, Collection<T>::add,
                               (r1, r2) -> { r1.addAll(r2); return r1; },
                               CH_ID);
}

将Stream流,还原一个指定的集合类型;

Supplier collectionFactory, 参数生产接口.没有入参,只有出参:

() -> { return new HashSet<>()}; –> 更改成方法引用.

// 将stream转换为字符串.
直接调用: Collectors.joining("分隔符");
Collectors.joining("分隔符", "前缀", "后缀");
​
// 示例:
String s = nameList.stream()
                .collect(Collectors.joining(",", "{", "}"));

{张三,张三,张三,张三,张三,李四,王五,赵六,田七,tom,jack,jerry,rose,齐天大圣}

6 StringJoiner

String

StringBuffer

StringBuilder

// String s1 = new String(“hello” + “world”);

StringJoiner

public StringJoiner(CharSequence delimiter, // 分隔符
                    CharSequence prefix, // 前缀
                CharSequence suffix) {} // 后缀
​
public StringJoiner(CharSequence delimiter) { // 分隔符
    this(delimiter, "", "");
}

添加串, 通过add(),可以链式调用;

Collectors, 这里边封装了大量的常用方法,该方法返回值就是实现了Collector接口的实现类的对象;

Stream对象 —> Map集合.

toMap(Function f1, Function f2)

7 分组实现

collect(Collectors.groupBy(Function f)

package com.tingyi.demo05;
​
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
​
/**
 * @author 听忆
 */
public class Test01 {
    public static void main(String[] args) {
        List<User> userList = new ArrayList<>();
        User u1 = new User(1L, "张三小", "男", "河北");
        User u2 = new User(15L, "tom", "男", "河北");
        User u3 = new User(14L, "赵小四", "女", "北京");
        User u4 = new User(13L, "马小六", "女", "北京");
        User u5 = new User(12L, "jack", "女", "上海");
        User u6 = new User(11L, "jerry", "女", "广州");
​
        userList.add(u1);
        userList.add(u2);
        userList.add(u3);
        userList.add(u4);
        userList.add(u5);
        userList.add(u6);
​
        Map<String, List<User>> listMap = userList.stream()
                .collect(Collectors.groupingBy(user -> user.getAddress()));
​
        Set<Map.Entry<String, List<User>>> entries = listMap.entrySet();
        for (Map.Entry<String, List<User>> entry : entries) {
            String key = entry.getKey();
            System.out.println(" key = " + key);
            List<User> users = entry.getValue();
            for (User user : users) {
                System.out.println(user);
            }
            System.out.println("-----------------------------------------------------------------");
        }
    }
}
​

key,填入的是分组字段的值.

value, 将符合该分组的元素都存储到了List当中;

符合条件的分为一组, 不符合条件的分为一组;

package com.tingyi.demo05;
​
import javax.naming.Name;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
​
/**
 * @author 听忆
 */
public class Test02 {
    public static void main(String[] args) {
        List<User> userList = new ArrayList<>();
        User u1 = new User(1L, "张三小", "男", "河北");
        User u2 = new User(15L, "tom", "男", "河北");
        User u3 = new User(14L, "赵小四", "女", "北京");
        User u4 = new User(13L, "马小六", "女", "北京");
        User u5 = new User(12L, "jack", "女", "上海");
        User u6 = new User(11L, "jerry", "女", "广州");
​
        userList.add(u1);
        userList.add(u2);
        userList.add(u3);
        userList.add(u4);
        userList.add(u5);
        userList.add(u6);
​
        /**
         * 将整个集合当中的分为两组.key = boolean值;
         */
        Map<Boolean, List<User>> listMap = userList.stream()
                .collect(Collectors.partitioningBy(user -> user.getSex().equals("男")));
​
        // forEach
        listMap.forEach((k, v) -> {
            System.out.println("key  = " + k);
            v.forEach(System.out::println);
            System.out.println("=====================================");
        });
    }
}
​

==总结分组实现==

前置条件都是在数据收集的时候去操作,也就是调用collect这个方法的时候,去操作分组的;

  • 分为两组, 根据条件判断,符合条件的分为一组.不符合条件的分为另外的一组;

  • 分为多组, 根据分组字段进行分组.符合字段要求的放到一个List集合.

8 聚合统计

方法如下所示:

package com.tingyi.demo06;
​
import java.util.ArrayList;
import java.util.IntSummaryStatistics;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
​
/**
 * @author 听忆
 */
public class Test {
    public static void main(String[] args) {
        List<Integer> numberList = new ArrayList<>();
        numberList.add(10);
        numberList.add(20);
        numberList.add(314);
        numberList.add(8);
        numberList.add(90);
​
        // ?????? Optional , Objects
        Optional<Integer> optional = numberList.stream()
                // .collect(Collectors.maxBy((i1, i2) -> i1 - i2));
                .max((i1, i2) -> i1 - i2); // Stream流当中提供的方法,更加直观一些.推荐使用;
        Integer result = optional.get();
        System.out.println(result);
        System.out.println("--------------------------------------------------");
        IntSummaryStatistics statistics = numberList.stream()
                .collect(Collectors.summarizingInt(i -> i));
​
        System.out.println(statistics.getCount());
        System.out.println(statistics.getAverage());
        System.out.println(statistics.getMin());
        System.out.println(statistics.getSum());
        System.out.println(statistics.getMax());
    }
}
​
System.out.println("------------------------------------------------");
LongSummaryStatistics summaryStatistics = userList.stream()
    .collect(Collectors.summarizingLong(User::getId));// user -> user.getId();
​
System.out.println(summaryStatistics.getAverage());
System.out.println(summaryStatistics.getCount());
System.out.println(summaryStatistics.getMax());
System.out.println(summaryStatistics.getMin());
System.out.println(summaryStatistics.getSum());

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐