【Java8新特性】——强大的Stream API

本文详细介绍Java8中的StreamAPI,包括Stream的概念、创建方法、并行流的应用、中间操作和终止操作等内容,并通过实例演示如何使用StreamAPI简化代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前提

    Java8中除了lambda表达式,Stream API(java.util.stream.*)也是神一样的存在,尤其在简化代码,提高代码质量上,十分推荐使用!

何为Stream?

简单说,Stream API提供高效而且易于使用的处理数据方式。Stream是Java8中处理集合的关键抽象概念,可以对集合进行非常复杂的查找,过滤和映射等操作。利用Stream API对集合数据进行操作,类似于SQL执行的数据库查询,而且还可以使用Stream API进行并行执行处理。那么Steam API到底是什么?是数据渠道,用于操作数据源(集合,数组等)所生成的元素序列。如果说集合讲的的数据,那么流讲的就是计算!

注意:

1、Stream自身不存储元素

2、Steam不改变源对象,会返回一个持有结果的新Stream

3、Stream操作是延迟执行的。会等到需要结果的时候才开始执行

执行步骤

1、创建Stream

一个数据源,获取一个流

2、中间操作

一个中间操作链,对数据源的数据进行处理

3、终止操作(终端操作)

一个终止操作,执行中间操作链,并产生结果

创建Stream

1、Java8中Collection接口被扩展,提供两种获取流的方法 stream() 返回一个顺序流;parallelStream()返回一个并行流

2、Arrays中静态方法stream()获取数组流

3、通过stream类中的静态方法of()

4、通过stream类中静态方法iterate()和generate()创建无限流

//     1、可以通过Collection系列集合提供的stream()或parallelStream()(并行流)
        List<String> list = new ArrayList<>();
        Stream<String> stream1 = list.stream();
//        parallelStream()(并行流)
        Stream<String> parallelStream = list.parallelStream();

//        2、通过Arrays中静态方法stream()获取数组流
        Employess[] emps = new Employess[10];
        Stream<Employess> stream2 = Arrays.stream(emps);

//        3、通过stream类中的静态方法of()
        Stream<String> stream3 = Stream.of("aa", "bb", "cc");

//        4、创建无限流
//        迭代
        Stream<Integer> stream4 = Stream.iterate(0, (x) -> x + 2).limit(10);
//        生成
        Stream.generate(() -> Math.random()).limit(5);

并行流是把内容分成多个数据块,并用不同的线程分别处理每个数据块的流。

Java8 中将并行进行优化,通过stream API使用parallel和sequential()在并行流和顺序流中切换。

Fork/Join框架:在必要情况下,将大任务拆分若干个小任务(拆到不可再拆时),再将一个个小任务运算的结果进行join汇总

相对于一般的线程池实现,fork/join框架的优势体现在对其中包含的任务的处理方式上.在一般的线程池中,如果一个线程正在执行的任务由于某些原因无法继续运行,那么该线程会处于等待状态.而在fork/join框架实现中,如果某个子问题由于等待另外一个子问题的完成而无法继续运行.那么处理该子问题的线程会主动寻找其他尚未运行的子问题来执行.这种方式减少了线程的等待时间,提高了性能 。

/**
     * 使用FrokJoin框架
     */
    @Test
    public void test1() {

        Instant start = Instant.now();
        ForkJoinPool pool = new ForkJoinPool();
        ForkJoinCalculate task = new ForkJoinCalculate(0, 10000000000L);
        Long sum = pool.invoke(task);
        System.out.println(sum);
        Instant end = Instant.now();
        System.out.println("耗费时间:"+Duration.between(start, end).toMillis());
    }

    /**
     * 普通for
     *
     * 注意两者相比数据量特别大的时候,使用FrokJoin框架更有效,因为Frok/Join 需要对任务拆分会花费一定时间,如果
     * 数据量较小的时候可能拆分时间比运行时间花费更多
     */
    public void test2() {
        Instant start = Instant.now();
        long sum = 0L;
        for (int i = 0; i < 10000000000L; i++) {
            sum +=i;
        }
        System.out.println(sum);
        Instant end = Instant.now();
        System.out.println("耗费时间:"+Duration.between(start, end).toMillis());
    }

    /**
     * Java8 并行流
     */
    @Test
    public void tes3() {
        Instant start = Instant.now();
//        parallerl Java8并行流
        LongStream.rangeClosed(0, 10000000000L)
                .parallel()
                .reduce(0, Long::sum);
        Instant end = Instant.now();
        System.out.println("耗费时间:"+Duration.between(start, end).toMillis());
    }

中间操作

多个中间操作可以连接形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理,而且在终止操作时一次性全部处理完,称为“惰性求值”

//代码块所用到实体和集合
@Data
public class Employess {

    private Integer code;

    private String name;

    private int age;

    private Double money;

    private Status status;

    public Employess(Integer code, String name, int age, Double money) {
        this.code = code;
        this.name = name;
        this.age = age;
        this.money = money;
    }

    public Employess(Integer code, String name, int age, Double money, Status status) {
        this.code = code;
        this.name = name;
        this.age = age;
        this.money = money;
        this.status = status;
    }
    @Override
    public String toString() {
        return "Employess{" +
                "code=" + code +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", money=" + money +
                ", status=" + status +
                '}';
    }
    public Employess() {
    }
    public Employess(Integer code) {
        this.code = code;
    }
    public enum Status {
        FREE,
        BUSY,
        VOCATION;
    }
}
List<Employess> emps = Arrays.asList(
            new Employess(101, "张三", 18, 9999.99),
            new Employess(102, "李四", 59, 6666.66),
            new Employess(103, "王五", 28, 3333.33),
            new Employess(104, "赵六", 8, 7777.77),
            new Employess(105, "田七", 38, 5555.55),
            new Employess(105, "田七", 38, 5555.55)
);

1、筛选与切片

filter(Predicate p):接收lambda,从流中排除某些元素

limit(long maxSize):截断流,使其得到元素不会超过给定数量

skip(long n):跳过元素,返回之前扔掉前n个元素,如不足,则返回空流,与limit互补

distinct():筛选,根据流所生成元素的hashCode()和equals()去除重复元素

      //中间操作-filter
//        如果没有终止操作,中间操作本质上也是不执行的
        System.out.println("----------filter--------------");
        Stream<Employess> stream = emps.stream()
                //筛选年龄大于35,而且工资大于6000
                .filter((e) -> e.getAge() > 35)
                .filter((e -> e.getMoney() > 6000));
        //终止操作
        stream.forEach(System.out::println);
        System.out.println("----------limit--------------");
        //只需前两个元素
        emps.stream().limit(2).forEach(System.out::println);
        System.out.println("----------skip--------------");
        //去除前两个元素
        emps.stream().skip(2).forEach(System.out::println);
        System.out.println("----------distinct--------------");
        //去除重复元素
        emps.stream().distinct().forEach(System.out::println);

2、映射

map(Function f):接收lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会应用到每个元素上,并将其映射成一个新的元素

flatMap(Function f):接收lambda函数做参数,将流中每个值都转换为一个另一个流,然后把所有流连接成一个流

mapToDouble(ToDoubleFunction f):接收一个函数作为参数,将该函数应用到每个元素上,产生一个新的DoubleStream。

mapToLong(ToLongFunction f):接收一个函数作为参数,将该函数应用到每个元素上,产生一个新的LongStream。

mapToInt(ToIntFunction f):接收一个函数作为参数,将该函数应用到每个元素上,产生一个新的IntStream。

(flatMap也有上述三种类型的stream)

 // map从集合中提取元素,把应用函数之后的元素取出放到新的stream中
        System.out.println("-----------map---------------");
        emps.stream()
                .map(Employess::getName)
                .forEach(System.out::println);
        System.out.println("-----------map函数 VS flatMap函数---------------");
        List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
        System.out.println("-----------map函数---------------");
//        map用于从集合中提取元素,将整个集合都放到Map中,类似list.add(list);
        Stream<Stream<Character>> mapStream = list.stream().map(TestStreamAPI2::filterCharacter);
        //返回值为 Stream<Stream<Character>> mapStream 类似于{{a,a,a},{b,b,b},{c,c,d}}
        mapStream.forEach((s)-> {
            System.out.print("{");
            s.forEach(System.out::print);
            System.out.print("}");
        });
        System.out.println();
        System.out.println("-----------flatMap函数---------------");
//        将流中每个元素都整合flatMap这个流中 list.addAll(list)
        Stream<Character> flatMapStream = list.stream().flatMap(TestStreamAPI2::filterCharacter);
        //返回值为 Stream<Character> {a,a,a,b,b,b,c,c,c}
        flatMapStream.forEach(System.out::print);


3、排序

sorted():产生一个新流,按自然顺序排序

sorted(Comparator comp):产生一个新流,其中按比较器顺序排序

       System.out.println("------------soreted------------------");
        emps.stream().map(Employess::getName).distinct().sorted().forEach(System.out::println);
        System.out.println("------------sorted(Comparator com)------------------");
        emps.stream().distinct().sorted((x, y) -> {
            if (x.getAge() == y.getAge()) {
                return x.getName().compareTo(y.getName());
            } else {
                return Integer.compare(x.getAge(), y.getAge());
            }
        }).forEach(System.out::println);

终止操作

终止操作会从流的流水线生成结果。结果可以是任何不是流的值。

 查找与匹配
* allMatch-检查是否匹配所有元素
* anyMatch-检查是否至少匹配一个元素
* noneMatch-检查是否没有匹配所有元素
* findFirst-返回第一个元素
* findAny-返回当前流中的任意元素
* count-返回流中元素的总个数
* max-返回流中最大值

* min-返回流中最小值

 boolean b1 = emps.stream()
                .allMatch(e -> e.getCode().equals("101"));
 System.out.println(b1);

归约

reduce(T identity,BinaryOperator)/reduce(BinaryOperator)——可以将流中元素反复结合起来,得到一个值

        System.out.println("-------------reduce(T identity,BinaryOperator)--------------");
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        Integer sum = list.stream()
                .reduce(0, (x, y) -> x + y);
        System.out.println(sum);

        System.out.println("--------------map-reduce--------------");
//        map 和reduce的连接通常为map-reduce模式
        Double slary = emps.stream()
                .map(Employess::getMoney)
                .reduce(0.0, (x, y) -> x + y);
        System.out.println(slary);

        System.out.println("-----------reduce(BinaryOperator)-----------------");
        Optional<Double> op = emps.stream()
                .map(Employess::getMoney)
                .reduce(Double::sum);
        System.out.println(op.get());

收集

collect——将流转换为其他形式,接收一个Collector接口的实现,用于给Stream中元素做汇总方法,这个特别强大,下面小编列举简单的使用方式。

存放到指定的集合中

//放到list中
        System.out.println("--------list-----------------");
        List<String> list = emps.stream()
                .map(Employess::getName)
                .collect(Collectors.toList());
        list.forEach(System.out::println);

        //可以将数据存放到指定集合中
        System.out.println("--------自定义集合-----------------");
        HashSet<String> hashSet = emps.stream()
                .map(Employess::getName)
                .collect(Collectors.toCollection(HashSet::new));
        hashSet.forEach(System.out::println);
可以通过这种方式获取平均值,总数,个数,最大值,最小值之类内容
//获取总数
        Long count = emps.stream()
                .collect(Collectors.counting());
        System.out.println(count);
//        求平均数
        Double ave = emps.stream()
                .collect(Collectors.averagingDouble(Employess::getMoney));
        System.out.println(ave);

分组

      //普通分组
        System.out.println("-------------普通分组-------------");
        Map<Employess.Status, List<Employess>> map1 = emps.stream()
                .collect(Collectors.groupingBy(Employess::getStatus));
        System.out.println(map1);
        //多级分组
        System.out.println("-------------多级分组-------------");
        Map<Employess.Status, Map<String, List<Employess>>> map = emps.stream().collect(Collectors.groupingBy(Employess::getStatus, Collectors.groupingBy((e) -> {
            if (e.getAge() >= 60) {
                return "老年";
            } else if (e.getAge() >= 35) {
                return "中年";
            } else {
                return "青年";
            }
        })));
        System.out.println(map);

分区

 Map<Boolean, List<Employess>> map = emps.stream().collect(Collectors.partitioningBy((e) -> e.getMoney() > 8000));
        System.out.println(map);

拼接

 String str = emps.stream()
             .map(Employess::getName)
             .collect(Collectors.joining(",","==","==="));

总结

    Stream API强大的功能还有很多,具体的还要详细学习API文档,看了Stream API的使用教程,小编脑海只有一个想法,还是默默地去重构自己的代码,才理解为什么别人那么嫌弃自己的代码,汗颜啊!

评论 53
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mandy_i

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值