java8 新特性

1.Lambda 表达式:

  Lambda 表达式将函数当成参数传递给某个方法,或者把代码本身当作数据处理;
    语法格式:
    1.1.用逗号分隔的参数列表
    1.2.-> 符号
    1.3.和语句块组成

  演示

		//1 完整的格式
        Arrays.asList("a", "b", "c" ,"d").forEach((x) -> {
            System.out.println(x);
        });
        //语句块只有一句时,可省略{}
        Arrays.asList("a", "b", "c" ,"d").forEach((x) -> System.out.println(x));
        //参数只有一个时,可以省略()
        Arrays.asList("a", "b", "c" ,"d").forEach(x -> System.out.println(x));
        //方法引用,还可以更加简化
        Arrays.asList("a", "b", "c" ,"d").forEach(System.out::println);
        //等价于
        List<String> list = Arrays.asList("a", "b", "c" ,"d");
        for(String e : list){
            System.out.println(e);
        }

2.方法引用:

  方法引用使得开发者可以直接引用现存的方法、Java类的构造方法或者实例对象。
  方法引用和Lambda表达式配合使用,使得java类的构造方法看起来紧凑而简洁,没有很
  多复杂的模板代码。

演示

package com.algorithm;

import java.util.function.Supplier;

public class Student {
    private String studentNo; //学号
    private String name;
    private Integer age;
    private String classGrade;
    private Integer score;
    public Student(){}
    public Student(String studentNo,String name,Integer age,String classGrade,Integer score){
        this.studentNo = studentNo;
        this.name = name;
        this.age = age;
        this.classGrade = classGrade;
        this.score = score;
    }

    public static Student create(Supplier<Student> supplier) {
        return supplier.get();
    }

    public static void printScore(Student student) {
        System.out.println( "score = " + student.getScore() );
    }

    public void printStudent() {
        System.out.println(this.toString() );
    }

    public String getStudentNo() {
        return studentNo;
    }

    public void setStudentNo(String studentNo) {
        this.studentNo = studentNo;
    }

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

    public String getClassGrade() {
        return classGrade;
    }

    public void setClassGrade(String classGrade) {
        this.classGrade = classGrade;
    }

    public Integer getScore() {
        return score;
    }

    public void setScore(Integer score) {
        this.score = score;
    }
}

  2.1.构造器引用 语法是Class::new (注意:这个构造器没有参数)

		Student student1 = Student.create(Student::new);
        //等价于
        Student student2 = Student.create(() -> new Student());

  2.2.静态方法引用 语法是Class::static_method(注意:这个方法接受一个Student类型的参数)

		//default void forEach(Consumer<? super T> action) {}
        students.forEach(Student::printScore);
        //等价于
        students.forEach(s -> Student.printScore(s));

        //2.3.成员方法的引用   语法是Class::method (注意,这个方法没有定义入参)
        students.forEach(Student::printStudent);
        //等价于
        students.forEach(s -> s.printStudent());

  将1到n的数字放入到长度为n+1的数组中,最好情况,假如将1到n的数组依次放入数
  组中,则数组最后一个槽位就会为空,要填满整个数组则要从1到n的数字中选一个填
  入到最后。所以必然会有重复的数字出现。

3.函数式接口:

  1.接口中只能有一个接口方法
  2.可以有静态方法和默认方法
  3.使用 @FunctionalInterface 标记
  4.默认方法可以被覆写

  我们基本不需要定义自己的函数式接口,Java8 已经给我们提供了大量的默认函数式接
  口,rt.jar 包的 java.util.function目录下可以看到所有默认的函数式接口,大致分为几类.

 3.1.Function<T, R> T 作为输入,返回的 R 作为输出

package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface Function<T, R> {

    /**
     * 该方法是接口的核心方法,定义该函数的业务逻辑
     */
    R apply(T t);

    /**
     * 设置在业务逻辑方法(apply)执行之前的预处理,类似于前置切面,当通过compose方式设置了前置方法,在
     * 调用apply时,会先执行compose设置的方法,因此compose设置的方法的入参类型需要和apply的入参类型一
     * 致,而compose设置的方法的出参将作为apply方法的入参,所以compose设置的方法的出参类型也要和apply
     * 方法的入参一致。     
     */
    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

    /**
     * 该方法和compose相反,设置在业务逻辑方法(apply)执行之后的处理逻辑,类似于后置切面,当通过andThen
     * 方式设置了后置方法,在调用apply执行完成之后,会执行andThen设置的方法,因此andThen设置的方法的入
     * 参类型需要和apply的出参类型一致,而andThen方法的出参将作为apply方法的最终出参,所以andThen的出
     * 参类型也要和apply方法的出参一致。
     */
    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

    /**
     * identity方法会返回一个不进行任何处理的Function,即输出与输入值相等;
     */
    static <T> Function<T, T> identity() {
        return t -> t;
    }
}

演示

		//3.1.Function<T,R> T 作为输入,返回的 R 作为输出
        Function<Student,Integer> f0 = (s) -> {
            return s.getAge();
        };
        //简化后
        Function<Student,Integer> f1 = s -> s.getAge();
        //apply方法
        Integer result1 = f1.apply(new Student("5","student5",13,"3",100));
        System.out.println(result1);
        Function<Integer, Integer> f2 = f -> f + 2;
        Function<Integer, Integer> f3 = f -> f * 2;
        /**
         * compose方法
         * 表示在执行f2时,先执行f3,并且执行f3时使用f3的输出作为输入
         * 等同于:
         * Integer x = f3.apply(2);
         * System.out.println(f2.apply(x));
         */
        Integer result2 = f2.compose(f3).apply(2);
        System.out.println(result2);
        /**
         * andThen方法
         * 表示执行f2的apply方法后使用其返回的值当作输入再执行f3的apply方法
         * 等同于:
         * Integer x = f2.apply(2);
         * System.out.println(f3.apply(x));
         */
        Integer result3 = f2.andThen(f3).apply(2);
        System.out.println(result3);
        /**
         * identity方法会返回一个不进行任何处理的Function,即输出与输入值相等;
         */
        Object result4 = Function.identity().apply("input");
        System.out.println(result4);

 3.2.Predicate<T> T 作为输入 ,返回 boolean 值的输出

  该接口用来定义逻辑判断的函数,类似于单元测试的Assert。通过实现Predicate来定义
  测试的标准,返回是否测试通过,核心方法就是test,传入待测试的值,返回测试结果
  (boolean类型)

package java.util.function;
import java.util.Objects;

@FunctionalInterface
public interface Predicate<T> {

    /**
     *  测试方法,也是该接口的核心方法,根据测试的规则,判断输入的参数是否测试通过
     */
    boolean test(T t);

    /**
     * 调用当前Predicate的test方法之后再去调用other的test方法,相当于进行两次判断
     * 并且的意思。两个条件都满足返回true
     */
    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

    /**
     * 对当前判断进行取非操作
     */
    default Predicate<T> negate() {
        return (t) -> !test(t);
    }

    /**
     * 对当前判断进行取或操作
     */
    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

    /**
     * 对当前操作进行取等操作,注意是一个静态方法
     * 该方法主要是用作将一个equals判断封装成函数对象,简化Predicate实例的创建
     */
    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}

演示

		List<Student> students = new ArrayList<>();
        students.add(new Student("1","student1",10,"1",80));
        students.add(new Student("2","student2",11,"1",90));
        students.add(new Student("3","student3",12,"2",85));
        students.add(new Student("4","student4",12,"2",90));
		//3.2.Predicate<T> T 作为输入 ,返回 boolean 值的输出
        //test方法
        Predicate<Integer> predicate1 = x -> x > 0;
        predicate1.test(30);
        //and方法
        Predicate<Integer> predicate2 = x -> x > 1;
        predicate1.and(predicate2).test(30);//两者条件均满足返回true
        //一般简化为
        predicate1.and(x -> x > 1).test(30);
        //or方法
        predicate1.or(predicate2).test(30);
        //一般简化为
        predicate1.or(x -> x > 1).test(30);
        //negate方法
        predicate1.negate().test(30);
        //isEqual方法
        Predicate<Integer> predicate3 = Predicate.isEqual(30);
        predicate3.test(30);
        //对空对象的判断
        Predicate<Object> isNull = Predicate.isEqual(null);
        isNull.test(null);
        //Stream<T> filter(Predicate<? super T> predicate);
        Predicate<Student> predicate4 = student -> student.getAge() > 10;
        //filter方法的入参就是Predicate
        List<Student> list1 = students.stream().filter(predicate4).collect(Collectors.toList());//获取年龄大于10的对象
        List<Student> list2 = students.stream().filter( s-> s.getAge() > 10).collect(Collectors.toList());//一般不预定义

 3.3.Consumer<T> T 作为输入 ,没有输出

  顾名思义,实现该接口的类主要是用来消费数据,但是不返回结果,因此,在该接口的
  内部可以通过异步来实现业务的处理,而不影响数据的消费。该接口只支持一个参数。
  该接口和Function接口的主要区别是没有返回值。

package java.util.function;
import java.util.Objects;

@FunctionalInterface
public interface Consumer<T> {

    /**
     * 该方法是接口的核心方法,定义该函数的业务逻辑
     */
    void accept(T t);

    /**
     * 设置在业务逻辑方法(accept)执行之后的处理逻辑,类似于后置切面,当通过andThen方式设置了后置方法,
     * 在调用accept执行完成之后,会执行andThen设置的方法。
     */
    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

演示

	//3.3.Consumer<T> T 作为输入 ,没有输出
	Consumer<String> consumer1 = str -> System.out.println("str : " + str);
    Consumer<String> consumer2 = str -> System.out.println(str + "hello world");
    consumer1.accept("java,");
    consumer1.andThen(consumer2).accept("java,");
    consumer1.andThen(s -> System.out.println(s + "hello world")).accept("java,");

 3.4.Supplier<R> 没有输入 , R 作为输出

  顾名思义,该接口为声明一个供应商,用来获取指定泛型的实例,类似于工厂,执行该
  接口的获取实例方法(get)不支持接收参数。

package java.util.function;
@FunctionalInterface
public interface Supplier<T> {

    /**
     * 调用get方法返回一个T类型对象
     */
    T get();
}

演示

		//3.4.Supplier<R> 没有输入 , R 作为输出
        Supplier<String> supplier1 = () -> { return "abc";};
        supplier1.get();
        //简化后
        Supplier<String> supplier2= () -> "abc";
        Supplier<String> supplier3 = String::new;
        System.out.println(supplier3.get());

        Supplier<Student> supplier4 = () -> new Student("5","student5",13,"3",100);
        supplier4.get();

前面演示几种常用的函数式接口,其他接口操作类似

 3.5. BiConsumer<T,U> 代表了一个接受两个输入参数的操作,并且不返回任何结果

 3.6. BiFunction<T,U,R> 代表了一个接受两个输入参数的方法,并且返回一个结果

 3.7. BinaryOperator<T> 代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果

 3.8. BiPredicate<T,U> 代表了一个两个参数的boolean值方法

 3.9. BooleanSupplier 代表了boolean值结果的提供方

 3.10. DoubleBinaryOperator 代表了作用于两个double值操作符的操作,并且返回了一个double值的结果。

 3.11. DoubleConsumer 代表一个接受double值参数的操作,并且不返回结果。

 3.12. DoubleFunction<R> 代表接受一个double值参数的方法,并且返回结果

 3.13. DoublePredicate 代表一个拥有double值参数的boolean值方法

 3.14. DoubleSupplier 代表一个double值结构的提供方

 3.15. DoubleToIntFunction 接受一个double类型输入,返回一个int类型结果。

 3.16. DoubleToLongFunction 接受一个double类型输入,返回一个long类型结果

 3.17. DoubleUnaryOperator 接受一个参数同为类型double,返回值类型也为double 。

 3.18. IntBinaryOperator 接受两个参数同为类型int,返回值类型也为int 。

 3.19. IntConsumer 接受一个int类型的输入参数,无返回值 。

 3.20. IntFunction<R>接受一个int类型输入参数,返回一个结果 。
 …

4.Streams:

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

  特点:
  1 . 不是数据结构,不会保存数据。而是按照特定的规则对数据进行计算,一般会输出
   结果。
  2. 不会修改原来的数据源,它会将操作后的数据保存到另外一个对象中。通常情况下会
   产生一个新的集合或一个值。
  3. 延迟执行特点,只有调用终端操作时,中间操作才会执行。

  stream(顺序流)和parallelStream(并行流)的区分:
  stream是顺序流,由主线程按顺序对流执行操作,而parallelStream是并行流,内部以多
  线程并行执行的方式对流进行操作,但前提是流中的数据处理没有顺序要求。如果流中
  的数据量足够大,并行流可以加快处速度。

  stream使用分为中间操作和终端操作:
  中间操作:filter,map,collection,sorted…
  终端操作:foreach,find,match,reduce,max,min,count…

4.1 Stream的创建

		//4.1.1.调用java.util.Collection.stream() 方法创建流。
        List<String> array = new ArrayList<>();
        Stream<String> stream = array.stream();// 创建一个顺序流
        Stream<String> parallelStream = array.parallelStream();// 创建一个并行流

        //4.1.2.调用java.util.Arrays.stream(T[] array)方法创建流
        int[] intArray = {1,2,3};
        Integer[] integerArray = new Integer[3];
        IntStream intStream = Arrays.stream(intArray);
        Stream IntegerStream = Arrays.stream(integerArray);

        //4.1.3.调用Stream的静态方法:of()、iterate()、generate() 创建流
        Stream<Integer> s1 = Stream.of(1, 2, 3, 4, 5, 6);//of()
        Stream<Integer> s2 = Stream.iterate(0, x -> x + 3).limit(6);//iterate()
        s2.forEach(System.out::println); // 0 2 4 6 8 10
        Stream<Double> s3 = Stream.generate(Math::random).limit(3);

4.2 流的筛选与切片

  4.2.1 filter:过滤流中的某些元素
  4.2.2 limit(n):获取n个元素
  4.2.3 skip(n):跳过n元素,配合limit(n)可实现分页
  4.2.4 distinct:通过流中元素的 hashCode() 和 equals() 去除重复元素

		Stream<Integer> s4 = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 8);
        Stream<Integer> s5 = s4.filter(s -> s > 4)//5,6,7,8,8
                .distinct() //5,6,7,8
                .skip(2) //7,8
                .limit(1); //7
        s5.forEach(System.out::println);

4.3 映射

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

		List<String> list3 = Arrays.asList("a,b,c", "1,2,3");
        Stream<String> s6 = list3.stream().map(s -> s.replaceAll(",", ""));
        s6.forEach(System.out::println); // abc  123

        Stream<String> s7 = list3.stream().flatMap(s -> Arrays.stream(s.split(",")));
        s7.forEach(System.out::println); // a b c 1 2 3

4.4 排序

  4.4.1 sorted():自然排序,流中元素需实现Comparable接口
  4.4.2 sorted(Comparator com):定制排序,自定义Comparator排序器

		List<Student> students = new ArrayList<>();
        students.add(new Student("1","student1",10,"1",80));
        students.add(new Student("2","student2",11,"1",90));
        students.add(new Student("3","student3",12,"2",85));
        students.add(new Student("4","student4",12,"2",90));
		List<String> list4 = Arrays.asList("a", "c", "b");
        //String 类自身已实现Compareable接口
        list4.stream().sorted().forEach(System.out::println);// a b c
        //自定义排序:先按姓名升序,姓名相同则按年龄升序
        students.stream().sorted(
                (o1, o2) -> {
                    if (o1.getName().equals(o2.getName())) {
                        return o1.getAge() - o2.getAge();
                    } else {
                        return o1.getName().compareTo(o2.getName());
                    }
                }
        ).forEach(System.out::println);

4.5 聚合、匹配操作(终端操作)

  4.5.1 max:返回流中元素最大值
  4.5.2 min:返回流中元素最小值
  4.5.3 count:返回流中元素的总个数
  4.5.4 findFirst:返回流中第一个元素
  4.5.5 findAny:返回流中的任意元素
  4.5.6 noneMatch:当流中每个元素都不符合该断言时才返回true,否则返回false
  4.5.7 anyMatch:只要流中有一个元素满足该断言则返回true,否则返回false
  4.5.8 allMatch:当流中每个元素都符合该断言时才返回true,否则返回false

		List<Integer> list5 = Arrays.asList(1, 2, 3, 4);
		Integer max = list5.stream().max(Integer::compareTo).get(); //4
        Integer min = list5.stream().min(Integer::compareTo).get(); //1
        Integer findFirst = list5.stream().findFirst().get(); //1
        Integer findAny = list5.stream().findAny().get(); //2
        boolean noneMatch = list5.stream().noneMatch(e -> e > 5); //true
        boolean anyMatch = list5.stream().anyMatch(e -> e > 2);  //true
        boolean allMatch = list5.stream().allMatch(e -> e > 5); //false

4.6 归约操作

归约,也称缩减,顾名思义,是把一个流缩减成一个值,能实现对集合求和、求乘积和求最值操作。

Optional<T> reduce(BinaryOperator<T> accumulator):
 第一次执行时,accumulator函数的第一个参数为流中的第一个元素,第二个参数为流中
 元素的第二个元素;第二次执行时,第一个参数为第一次函数执行的结果,第二个参数
 为流中的第三个元素;依次类推。

T reduce(T identity, BinaryOperator<T> accumulator):
 流程Optional<T> reduce(BinaryOperator<T> accumulator)一样,只是第一次执行时,
 accumulator函数的第一个参数为identity,而第二个参数为流中的第一个元素。

<U> U reduce(U identity,BiFunction<U, ? super T, U> accumulator,BinaryOperator<U> combiner):
 在串行流(stream)中,该方法跟第二个方法一样,即第三个参数combiner不会起作用。在
 并行流(parallelStream)中,我们知道流被fork join出多个线程进行执行,此时每个线程的执
 行流程就跟第二个方法reduce(identity,accumulator)一样,而第三个参数combiner函数,
 则是将每个线程的执行结果当成一个新的流,然后使用第一个方法reduce(accumulator)流
 程进行规约。

		List<Integer> list6 = Arrays.asList(1, 2, 3, 4, 5);
        Integer integer1 = list6.stream().reduce((x1, x2) -> x1 + x2).get();
        Integer integer2 = list6.stream().reduce(2, (x1, x2) -> x1 + x2);
        Integer integer3 = list6.stream().reduce(0,(x1, x2) -> x1 - x2, (y1, y2) -> y1 * y2);
        Integer integer4 = list6.parallelStream().reduce(0,(x1, x2) -> x1 - x2, (y1, y2) -> y1 * y2);

4.7 收集操作

 4.7.1 collect:接收一个Collector实例,将流中元素收集成另外一个数据结构。

 4.7.2 Collector<T, A, R> 是一个接口,有以下5个抽象方法:

  4.7.2.1 Supplier<A> supplier():创建一个结果容器A
  4.7.2.2 BiConsumer<A, T> accumulator():消费型接口,第一个参数为容器A,第二个
     参数为流中元素T。
  4.7.2.3 BinaryOperator<A> combiner():函数接口,将并行流中各个子进程的运行结果
     合并。
  4.7.2.4 Function<A, R> finisher():函数式接口,参数为:容器A,返回类型为:collect
     方法最终想要的结果R。
  4.7.2.5 Set<Characteristics> characteristics():返回一个不可变的Set集合,用来表明
     该Collector的特征。
     有以下三个特征:
     CONCURRENT:表示此收集器支持并发。
     UNORDERED:表示该收集操作不会保留流中元素原有的顺序。
     IDENTITY_FINISH:表示finisher参数只是标识而已,可忽略。

		List<Student> students = new ArrayList<>();
        students.add(new Student("1","student1",10,"1",80));
        students.add(new Student("2","student2",11,"1",90));
        students.add(new Student("3","student3",12,"2",85));
        students.add(new Student("4","student4",12,"2",90));
        
		//转list
        List<Integer> ageList = students.stream().map(Student::getAge).collect(Collectors.toList());
        //转set
        Set<Integer> ageSet = students.stream().map(Student::getAge).collect(Collectors.toSet());
        //转map(注:key不能相同,否则报错)
        Map<String, Integer> map = students.stream().collect(Collectors.toMap(Student::getName, Student::getAge));
        //学生总数
        Long studentNum = students.stream().collect(Collectors.counting());
        //最大年龄
        Integer max = students.stream().map(Student::getAge).collect(Collectors.maxBy(Integer::compare)).get();
        //年龄求和
        Integer sumAge = students.stream().collect(Collectors.summingInt(Student::getAge));
        //平均年龄
        Double average = students.stream().collect(Collectors.averagingDouble(Student::getAge));
        //分组
        Map<Integer, List<Student>> ageMap = students.stream().collect(Collectors.groupingBy(Student::getAge));
        //按分数分组
        Map<Boolean, List<Student>> group1 = students.stream().collect(Collectors.partitioningBy(x -> x.getScore() > 90));
        //按班级分组
        Map<String, List<Student>> group2 = students.stream().collect(Collectors.groupingBy(Student::getClassGrade));
        //分区,分成两部分,一部分大于10岁,一部分小于等于10岁
        Map<Boolean, List<Student>> partMap = students.stream().collect(Collectors.partitioningBy(v -> v.getAge() > 10));
        //归约
        Integer allAge = students.stream().map(Student::getAge).collect(Collectors.reducing(Integer::sum)).get();
        //接合(joining)
        String names = students.stream().map(p -> p.getName()).collect(Collectors.joining(","));
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值