java基础回顾-Stream流常用api、串行和并行流源码分析

本文介绍了Java中default关键字和Stream流。default关键字在Java 8引入默认方法,解决了多继承冲突问题,其与Stream流相关,因集合体系庞大,在Collection接口用default定义stream()方法可避免架构改动。还阐述了Stream流是处理集合的关键概念,介绍其操作的三个步骤并给出代码示例。

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

一、default关键字

1、简述概念
default关键字:默认方法与冲突
在Java中有一条亘久不变的定理:类只能单继承,接口可以多实现。
Java8之前由于接口中的方法全部都是抽象方法,一个类如果同时实现两个拥有相同方法签名的接口并不会出现冲突,
实现类只需要重写该方法即可。但是在Java8中出现了默认方法,
这样一来就有可能会出现Java一直在避免的多继承问题——
一个类从多个地方(类或接口)继承了有相同方法签名的方法。
比如接口A有个方法和接口B的方法一样。一个实现类C实现了A和B,C的子类是D:
D调方法的优先级:C最大。

2、冲突

遇到这种情况时,我们需要遵循下面三条原则解决冲突:
一、类中方法优先级最高。类或父类中定义方法的优先级高于任何声明为默认方法的优先级。
二、第一条原则无法进行判断,子接口中声明的默认方法的优先级仅次于类中声明方法的优先级。
三、上述两条原则仍然无法判断,实现类必须显式重写方法或选择使用哪一个默认方法的实现。

3、jdk8的新特性与default的关联

JAVA8接口的新特性:
8之前只能定义常量和抽象方法。不能定义有方法体的方法。
8开始就可以定义
1、常量和抽象方法
2、静态方法,即static修饰的有具体实现的方法
3、default修饰的有具体实现的方法。
注意:static final修饰的变量还是不可以定义的。

4、进而联想default与Stream流的关系

这样改的原因,很可能是因为8的新特性 Stream 流 。
对集合而言,每种集合都需要有获取Stream对象的方法——stream()。
按照设计理念stream()方法应该在集合根接口Collection中声明,
然后让Collection的实现类重写该方法。
然而集合体系很庞大,
如果直接在Collection接口中声明stream()方法,
那么所有的Collection实现类都要重写该方法,
对现有的架构改动极大。
所以如果Collection接口中可以定义有具体实现的stream()方法就不会破坏现有的架构,
Collection的实现类只需要直接继承过来就可以了。
在JDK8的源码中,我们可以看到Collection接口中定义有default修饰的默认方法stream():

二、Stream

1、简述

Stream是Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API对集合数据进行操作,并且这个类很多方法的参数都是函数式接口。

2、步骤

Stream操作的三个步骤:
创建stream
中间操作(过滤、map)
终止操作

3、三大步骤的代码示例与讲解:

整个代码可以直接占到开发工具进行测试,根据注释进行进一步理解:

public class StreamTest1 {

    public static void main(String[] args) {
        List<Person> list= Arrays.asList(
                new Person("1",11,5000),
                new Person("2",21,65000),
                new Person("3",31,1200),
                new Person("6",61,26000),
                new Person("4",24,25800),
                new Person("4",41,25800)
        );
//一、 创建stream
        // 1,校验通过Collection 系列集合提供的stream()或者paralleStream()
        Stream<Person> stream1 = list.stream();
        Stream<Person> stream11 = list.stream();
        // 2.通过Arrays的静态方法stream()获取数组流
        String[] str = {"ss","23d","112","ss"};
        Stream<String> stream2 = Arrays.stream(str);
        // 3.通过Stream类中的静态方法of
        Stream<String> stream3 = Stream.of("aa","bb","cc","bb","dd");
        // 4.创建无限流
        // 迭代
        Stream<Integer> stream4 = Stream.iterate(0,(x) -> x+2);
        //生成
        Stream<Double> stream5 = Stream.generate(() -> Math.random());
//二、中间使用
        //1、筛选和切片
        //filter-接收Lambda,从流中排除某些元素。
        stream1.filter(person -> person.getAge() > 1).forEach(System.out::println);
        //stream1.limit(2).forEach(System.out::println);//报错,因为流中的信息只能操作一次,在进行终止操作后无法再次读取
        //limit-截断流, 使其元素不超过给定数量(取前n个元素)。
        stream2.limit(3).forEach(System.out::println);
        //skip(n) -返回一个扔掉了前n个元素的流(取第n个元素后面的元素)。若流中元素不足n个,则返回一个空流。与limit(n)互补
        //stream3.skip(3).forEach(System.out::println);
        //stream3.collect(Collectors.toList());//报错,forEach属于终止操作
        List<String> collect = stream3.skip(3).collect(Collectors.toList());
        //distinct-筛选排重, 通过流所生成元素的hashCode()和equals()去除重复元素
        // (如果想对实体类进行排重就需要实体类重写hashCode和equals这俩方法,
        // 否则哪个方法没重写,那个方法就会根据空间做吧比较得到不相等的结果而无法排重)
        stream11.distinct().forEach(System.out::println);
        //2、映射  Stream.flatMap,正如它的名称所猜测的,是map和一个flat行动。这意味着您首先对元素应用一个函数,然后将其扁平化。Stream.map只对流应用函数,而不对流进行平坦处理。
        //为了理解什么是扁平化,考虑一个像[[1,2,3],[4,5,6],[7,8,9]]这样的具有“两个层次”的结构。 扁平化意味着将其转化为“一个一级”结构:[1,2,3,4,5,6,7,8,9]。
        //map-接收Lambda ,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,
        // 并将其映射成一个新的元素。map的参数必须是Function,即传入一个参数返回一个参数
        //比如将集合中的每个元素传进去获取每个元素的年龄
        //map方法必须是一个参数返回一个参数,如果每个元素拆成数组,那也是多个数组.直接打印的话只能打印出每个数组的地址
        list.stream().map(p-> String.valueOf(p.getAge()).split("")).forEach(System.out::println);//[Ljava.lang.String;@3d646c37
        list.stream().map(p-> String.valueOf(p.getAge()).split("")).forEach(a->{
            System.out.print("[");
            for (int i = 0; i < a.length; i++) {
                if (i==a.length-1) System.out.print(a[i]);
                else System.out.print(a[i]+",");
            }
            System.out.print("] ");
        });//[1,1] [2,1] [3,1] [6,1] [2,4] [4,1]
        //flatMap-接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。最后所有的小元素都会汇集到一起在一个集合中
        System.out.println();//1 1 2 1 3 1 6 1 2 4 4 1
        list.stream().flatMap(p-> Stream.of(String.valueOf(p.getAge()).split(""))).forEach(a->{
            System.out.print(a+" ");
        });
        //3、排序
        String[] str2 = {"ss","23d","112","ss"};
        //list.stream().sorted().forEach(System.out::println);//报错:org.Person cannot be cast to java.lang.Comparable
        //sorted()-自然排序(Comparable)
        Arrays.stream(str2).sorted().forEach(System.out::println);
        //sorted(Comparator com)- 定制排序(Comparator)  比如根据工资从小到大排
        list.stream().sorted((p,p2)-> p.getSalary()>=p2.getSalary()?0:-1).forEach(System.out::println);
//三、终止操作:查询、匹配、foreach,toArray,collect,求和reduce
        //1、查找与匹配
        //allMatch-检查是否匹配所有元素--所有元素都为true才为true
        System.out.println(list.stream().allMatch((a) -> a.getAge() > 18));//false
        //anyMatch-检查 是否至少匹配一个元素--只要有一个返回true即为true
        System.out.println(list.stream().anyMatch((a) -> a.getAge() < 18));//true
        //noneMatch- -检查 是否所有元素都没有匹配-- 所有元素都不满足返回false,最后结果才为true
        System.out.println(list.stream().noneMatch((a) -> a.getAge() < 10));//false
        //findFirst-返回第一个元素
        System.out.println(list.stream().findFirst());
        //findAny-返图当前流中的任意元素--非高并发情况返回第一个
        System.out.println(list.stream().findAny());
        //2、计算
		//count-返回流中元素的总个数
        System.out.println(list.stream().count());
        //如果比较的结果为0则返回a1
        //max-返回流中最大值  里面的比较函数结果为正数的数(与其他数比)
        System.out.println(list.stream().max((a1,a2)-> a1.getAge()>=a2.getAge()?1:-1));
        System.out.println(list.stream().max(Comparator.comparingInt(Person::getAge)));
        //min-返回流中最个值  里面的比较函数结果为负数的数(与其他数比)
        System.out.println(list.stream().min(Comparator.comparingInt(Person::getAge)));
		//reduce-归纳,将流中元素反复按函数式参数(比如下面的(a,b)-> Long.sum(a, b))操作结合得到的结果
        System.out.println(LongStream.rangeClosed(0, 100000000L).reduce(0,(a,b)-> Long.sum(a, b)));
//四、串并行流
        //ForkJoin框架的简化版使用:使用 parallel(并行)/sequential(顺序串行流)来实现串行流和并行流
        //list.stream().map(a->a.getAge()).sequential().reduce(0,(a,b)-> Integer.sum(a, b));方法引用简化如下:
        Instant now = Instant.now();
        System.out.println(list.stream().map(a -> a.getAge()).sequential().reduce(0, Integer::sum)+"耗时"+Duration.between(now,Instant.now()));
        Instant now2 = Instant.now();
        System.out.println(list.stream().map(a -> a.getAge()).parallel().reduce(0, Integer::sum)+"耗时"+Duration.between(now2,Instant.now()));
        //最终耗时:串行是0.001s,并行是0.003s
        //因为数量较少,而并行需要开多线程再合并结果,因此会比串行慢。因此并行更适合大数量的计算
        //源码分析:java.util.stream.ReferencePipeline.reduce(P_OUT, java.util.function.BinaryOperator<P_OUT>)
        //-->java.util.stream.AbstractPipeline.evaluate(java.util.stream.TerminalOp<E_OUT,R>)
        //-->每个流的方法在返回时,都会判断是并行还是串行
        /**
         * isParallel()//是不是并行(默认不是并行)list.stream().map(a -> a.getAge()).reduce(0, Integer::sum)  这里没有指定就按默认的串行流走
         *   ? terminalOp.evaluateParallel(this, sourceSpliterator(terminalOp.getOpFlags()))
         *   : terminalOp.evaluateSequential(this, sourceSpliterator(terminalOp.getOpFlags()));
         */
        //如果是并行,则创建ForkJoinTask的子类:对应方法的任务类,比如这个ReduceTask
        //java.util.stream.ReduceOps.ReduceOp.evaluateParallel方法
        //return new ReduceTask<>(this, helper, spliterator).invoke().get();
        //如果是串行,则通过管道Sink<P_IN> wrappedSink负责开启和关闭流,中间通过遍历和划分源元素的对象Spliterator进行以此处理管道中的信息。
        /**
         * wrappedSink.begin(spliterator.getExactSizeIfKnown());
         * spliterator.forEachRemaining(wrappedSink);
         * wrappedSink.end();
         */
    }

}
class Person{
    String name;
    int age;
    int salary;
    public Person(){
        System.out.println("无参构造执行");
    }

    public Person(String name, int age, int salary) {
        this.name = name;
        this.age = age;
        this.salary = salary;
        System.out.println("有参构造执行");
    }

    public Person(int age){
        this.age=age;
        System.out.println("单参构造Function");
    }

    public Person(Person person) {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getSalary() {
        return salary;
    }

    public void setSalary(int salary) {
        this.salary = salary;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                salary == person.salary &&
                Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age, salary);
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", salary=" + salary +
                '}';
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值