java8 学习笔记

引言:几乎每个java应用都会创建和处理集合,但是集合的传统CollectionAPI处理需要写一堆代码,使用起来不尽人意还会使代码变得臃肿,这个时候java8提供的StreamAPI(内部迭代)会让一切变得简单的.java8最大的特性是引入了Lambda表达式,函数式编程,使用不可变值和函数,函数对不可变值进行处理,映射成另一个值。

1.函数式接口

使用@FunctionalInterface注解修饰的接口,代表只有一个抽象方法,用作lambda表达式的表达,函数式接口中可以有多个默认方法和静态方法。能够常见的函数式接口比如Runnable,Comparator…

2 常见的函数式接口

函数式接口

函数描述符

抽象方法

功能

示例

Predicate<T>

T->boolean

test(T t)

真假判断

Predicate<Integer> predicate = a -> a > 1;

Consumer<T>

T->void

accept(T t)

消费型函数

Consumer<String> consumer = a -> System.out.println(a);

Function<T,R>

T->R

R apply(T t)

将T映射为R

Function<Long,Integer> function = a -> a.intValue();

Supplier<T>

()->T

T get()

生产型函数

Supplier<String> supplier = () -> "test";

UnaryOperator<T>

T->T

继承自Function<T, T>

接收T对象,返回T对象

UnaryOperator<Employee> unaryOperator = employee -> employee.setSalary(100);

BinaryOperator<T>

(T,T)->T

继承自BiFunction<T,T,T>

接收两个T对象,返回T对象

BinaryOperator<Integer> binaryOperator = (a, b) -> a * b;

BiPredicate<L,R>

(L,R) -> boolean

test(T t,R r)

接收T和R对象,返回boolean值

BiPredicate<Integer, Integer> bi = (x, y) -> x > y;

BiConsumer<T,U>

(T,R) ->void

void accept(T t, U u)

接收T和U对象,不返回值

BiConsumer<String, String> biConsumer = (x, y) -> System.out.println(x + "===" + y);

BiFunction<T,U,R>

(T,U) ->R

R apply(T t, U u)

接收T和U对象,返回R对象

BiFunction<String, String, String> biFunction = (x, y) -> x + "===" + y;

public class Demo {

    public static void main(String[] args) {
        // Consumer
        Consumer<Integer> consumer = item -> System.out.println(item);
        Arrays.asList(1, 2, 3, 4).forEach(item -> consumer.accept(item));
        // Predicate
        Predicate<Integer> predicate = item -> item > 175;
        boolean test = predicate.test(180);
        // Function
        Function<Student, String> function = Student::getName;
        String name = function.apply(new Student("张三", 18));
        //UnaryOperator
        UnaryOperator<Boolean> unaryOperator = a -> !a;
        Boolean b = unaryOperator.apply(true);
        //BinaryOperator
        BinaryOperator<Integer> operator = (x, y) -> x * y;
        Integer result = operator.apply(1, 4);
    }

    @Data
    static class Student {
        private String name;
        private Integer age;

        public Student(String name, Integer age) {
            this.name = name;
            this.age = age;
        }
        public Student() {
        }
    }
}

3.方法引用

java8中有一种lambda表达式的写法叫方法引用,方法引用让你可以重复使用现有的方法定义,并像Lambda一样传递。在一些情况下,它显得更易读,清晰。

方法引用主要有三种

①指向静态方法的方法引用

比如Integer的parseInt方法,可以写成Integer::parseInt

②指向任意类型实例方法的方法引用

比如String的length方法,可以写成String::length,或者上面的Student::getName

③指向现有对象的实例方法的方法引用

比如有一个变量this,支持实例方法resolveData,然后就可以写成this::resolveData

4构造函数引用

构建一个新的实例对象

Supplier<Student> a1 = Student::new;
        Student student = a1.get();

等价于

Supplier<Student> a1 = () -> new Student();
        Student student = a1.get();

对于有参构造函数的话,适合Function接口

BiFunction<String,Integer,Student> a1 = Student::new;
        Student student = a1.apply("张三",18);

5 java8的流式处理

CollectionAPI需要用户去做迭代,如foreach,成为外部迭代。相反,StreamAPI使用内部迭代,帮你做了迭代,还把得到的流值存在了某个地方,你只要给出一个函数说要干嘛就可以了。

流的操作是惰性求值,分为中间操作和终端操作,在终端操作之前,中间操作返回的都是Stream,直到遇到终端操作才计算结果,而且流只能遍历一次,而且流中的元素是按需计算的。

中间操作:

filter: 参数=== Predicate<T>, 函数描述符=== T->Boolean

map:参数=== Function<T,R>,函数描述符=== T->R

limit:截断流

sorted:参数=== Comparator<T>,函数描述符=== (T,T) -> int

distinct:去重

终端操作:

forEach:消费流中的每个元素并对其应用指定lambda,这一操作返回void

count:返回流中的元素的个数,这一操作返回long

collect:把流归约成一个集合,比如List,Map甚至Integer

总而言之,流的使用一般包括三件事:⒈一个数据源来执行一个查询⒉一个中间操作链,形成一条流的流水线⒊一个终端操作,执行流水线,并能生成结果。

①filter,筛选。

筛选出学生中成年人的学生信息

ArrayList<Student> students = Lists.newArrayList(new Student("张三", 18), new Student("李四", 17), new Student("王五", 19));
        Predicate<Student> ageAbove18Predicate = s -> s.getAge() >= 18;
        List<Student> result = students.stream().filter(ageAbove18Predicate).collect(Collectors.toList());

②map,映射

直接查看学生的年龄信息

ArrayList<Student> students = Lists.newArrayList(new Student("张三", 18), new Student("李四", 17), new Student("王五", 19));
        Function<Student,Integer> ageFunction = Student::getAge;
        List<Integer> result = students.stream().map(ageFunction).collect(Collectors.toList());

③limit,截断流

查看年龄最小的两位学生的信息

ArrayList<Student> students = Lists.newArrayList(new Student("张三", 18), new Student("李四", 17), new Student("王五", 19));
        Comparator<Student> ageComparator = Comparator.comparing(Student::getAge);
        List<Student> result = students.stream().sorted(ageComparator).limit(2).collect(Collectors.toList());

④distinct,去重

按照年龄去重

ArrayList<Student> students = Lists.newArrayList(new Student("张三1", 18),new Student("张三", 18), new Student("李四", 17), new Student("王五", 19));
        List<Integer> result = students.stream().map(Student::getAge).distinct().collect(Collectors.toList());

⑤skip,跳过元素

按照学生列表顺序跳过第一个之后的剩余学生信息

ArrayList<Student> students = Lists.newArrayList(new Student("张三", 18), new Student("李四", 17), new Student("王五", 19));
        List<Student> result = students.stream().skip(1).collect(Collectors.toList());

⑥flatMap,流的扁平化

给定单词列表["Hello","World"],想要返回["H","e","l","o","W","r","d"]
String[] stringArray = {"Hello","World"};
        List<String> result = Arrays.stream(stringArray).map(w -> w.split("")).flatMap(Arrays::stream).distinct().collect(Collectors.toList());

⑦查找和匹配,allMathc,anyMatch,nonMatch,findAny,findFirst

有些操作不需要处理整个流就能得到结果,例如,你需要对一个用and连起来的大布尔表达式求值,不管表达式有多长,只要有一个表达式为false,就可以推断出整个表达式为false,就是短路。对于流而言,某些操作不需要处理整个流就能得到结果

找出第一个数的平方能被2整除的数

 List<Integer> integers = Arrays.asList(1, 2, 3, 5, 6);
        Integer first = integers.stream().map(x -> x * x).filter(x -> x % 2 == 0).findFirst().orElse(-1);

对于findFirst和findAny,主要是针对并行,如果你不关心返回的元素是哪个,请使用findAny

⑧归约

List<Integer> integers = Arrays.asList(1, 2, 3, 5, 6);
        Integer reduce = integers.stream().reduce(0, Integer::sum);

第一个参数是初始值,第二个参数是BinaryOperator,接收两个T类型数据,返回T类型数据

6 原始类型流特化

java8引入三个原始类型特化流接口解决暗含的装箱操作,分别为IntStream,DoubleStream,LongStream,分别将流中的元素特化为int,long,double

映射到数据流

ArrayList<Student> students = Lists.newArrayList(new Student("张三", 18), new Student("李四", 17), new Student("王五", 19));
        int sum = students.stream().mapToInt(Student::getAge).sum();

转换回对象流

ArrayList<Student> students = Lists.newArrayList(new Student("张三", 18), new Student("李四", 17), new Student("王五", 19));
        IntStream intStream = students.stream().mapToInt(Student::getAge);
        Stream<Integer> boxed = intStream.boxed();

7 无限流

StreamAPI提供了两个静态方法来从函数生成流,Stream.iterate和Stream.generate,这两个操作可以创建所谓的无限流。

迭代:

Stream.iterate(0, n -> n + 2).limit(10).forEach(System.out::println);

接收一个0的初始值,使用表达式n+2在前一个元素的基础上加上2生成新的元素作为下一个元素的新值。

著名的斐波那契数列生成:

Stream.iterate(new int[]{0, 1}, t -> new int[]{t[1], t[0] + t[1]}).limit(10).map(t -> t[0]).forEach(System.out::println);

生成:

与iterare类似,generate方法也可以按需生成一个无限流

Stream.generate(Math::random).limit(5).forEach(System.out::println);

generate接收一个Supplier参数,故斐波那契也可以使用generate生成

IntSupplier intSupplier = new IntSupplier() {
            private int previous = 0;
            private int next = 1;

            @Override
            public int getAsInt() {
                int oldPrevious = previous;
                int nextValue = previous + next;
                previous = next;
                next = nextValue;
                return oldPrevious;
            }
        };
        IntStream.generate(intSupplier).limit(10).forEach(System.out::println);

8 收集数据

①查找流中最大值和最小值

ArrayList<Student> students = Lists.newArrayList(new Student("张三", 18), new Student("李四", 17), new Student("王五", 19));
        Optional<Student> max1 = students.stream().collect(Collectors.maxBy(Comparator.comparing(Student::getAge)));
        Optional<Student> max2 = students.stream().max(Comparator.comparing(Student::getAge));
        Optional<Student> min1 = students.stream().collect(Collectors.minBy(Comparator.comparing(Student::getAge)));
        Optional<Student> min2 = students.stream().min(Comparator.comparing(Student::getAge));

②汇总

ArrayList<Student> students = Lists.newArrayList(new Student("张三", 18), new Student("李四", 17), new Student("王五", 19));
        IntSummaryStatistics collect = students.stream().collect(Collectors.summarizingInt(Student::getAge));

打印出的结果是

{"average":18.0,"count":3,"max":19,"min":17,"sum":54}

IntSummaryStatistics一次可以得到总和,平均值,最大值,最小值

③连接字符串

String collect1 = students.stream().map(Student::getName).collect(Collectors.joining());
String collect2 = students.stream().map(Student::getName).collect(Collectors.joining(","));
String collect3 = students.stream().map(Student::getName).collect(Collectors.joining(",","[","]"));

joining接收CharSequence delimiter分隔符和前缀、后缀。

④分组

按照年龄分组

ArrayList<Student> students = Lists.newArrayList(new Student("张三", 18), new Student("李四", 17), new Student("王五", 19), new Student("马六", 19));
        Map<Integer, List<Student>> collect3 = students.stream().collect(Collectors.groupingBy(Student::getAge));

结果为 {17:[{"age":17,"name":"李四"}],18:[{"age":18,"name":"张三"}],19:[{"age":19,"name":"王五"},{"age":19,"name":"马六"}]}

还可以多级分组。

ArrayList<Student> students = Lists.newArrayList(new Student("张三", 18), new Student("李四", 17), new Student("王五", 19), new Student("马六", 19), new Student("马六", 19));
        Map<Integer, Map<String, List<Student>>> collect1 = students.stream().collect(Collectors.groupingBy(Student::getAge, Collectors.groupingBy(Student::getName)));

⑤分区

学生年龄大于18和小于等于18的分区

ArrayList<Student> students = Lists.newArrayList(new Student("张三", 18), new Student("李四", 17), new Student("王五", 19), new Student("马六", 19), new Student("马六", 19));
        Map<Boolean, List<Student>> collect1 = students.stream().collect(Collectors.partitioningBy(item -> item.getAge() > 18));

结果 {false:[{"age":18,"name":"张三"},{"age":17,"name":"李四"}],true:[{"age":19,"name":"王五"},{"age":19,"name":"马六"},{"age":19,"name":"马六"}]}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值