Java高级编程知识—13、Java 8 新特性

13.1 Lambda表达式

  1. 举例:(o1,o2)->Integer.compare(o1,o2)

  2. 格式:

    ->:Lambda操作符 或 箭头操作符

    ->左边:Lambda形参列表(其实就是接口中的抽象方法的形参列表)

    ->右边:Lambda体(其实就是重写的抽象方法的方法体

  3. Lambda表达式的使用 :(6种情况)

  4. Lambda表达式的本质:作为函数式接口的实例,是一个匿名实现类的对象

  5. 只包含一个抽象方法的接口称为函数式接口,可以加注解@FunctionalInterface进行标注校验

总结:

  • 左边:
    • 形参列表的参数类型可以省略(类型推断)
    • 如果形参列表只有一个参数,小括号可以省略
  • 右边:如果Lambda体只有一条执行语句(可以是return语句),可以省略大括号{}以及return关键字

语法格式一:无参、无返回值

Runnable r = new Runnable() {
            @Override
            public void run() {
                System.out.println("helloworld");
            }
        };
Runnable r1 = ()->{System.out.println("helloworld");};

语法格式二:需要一个参数,但是没有返回值

Consumer<String> con = new Comsumer<String>(){
    @Override
    public void accept(Stirng s){
        System.out.println(s);
    }
};
Comsumer<String> con = (String s)->{System.out.println(s);};

语法格式三:数据类型可以省略,因为可由编译器推断得出,称为“类型推断”

Comsumer<String> con = (s)->{System.out.println(s);};

语法格式四:Lambda若只需要一个参数,参数的小括号可以省略

Comsumer<String> con = s->{System.out.println(s);};

语法格式五:Lambda需要多个参数,多条执行语句,并且有返回值

Comparator<Integer> com = (o1,o2) -> {
		sout(o1);
		sout(o2);
		return o1.compareTo(o2);
}

语法格式六:当Lambda体只有一条语句时,return与大括号都可以省略

Comparator<Integer> com = (o1,o2)->o1.compareTo(o2);

方法引用

Comparator<Integer> comparator2 = Integer :: compare;
System.out.println(comparator2.compare(10,20));

13.2 函数式接口

  • 消费型接口 Comsumer< T > void accept(T t)
  • 供给型接口 Suppelier< T > T get()
  • 函数型接口 Function< T,R > R apply(T t)
  • 断定型接口 Perdicate< T > boolean test(T t)

消费型接口举例:

    public void happyTime(double money, Consumer<Double> con){
        con.accept(money);
    }
	@Test
    public void test1(){
        happyTime(500, new Consumer<Double>() {
            @Override
            public void accept(Double aDouble) {
                System.out.println("消费了" + aDouble);
            }
        });
//        Lambda写法
        happyTime(600, o-> System.out.println("lambda消费了"+o));
    }

供给型接口举例:

//    根据给定的规则,规律集合中的字符串,此规则由Predicate的方法决定
    public List<String > filterString(List<String> list, Predicate<String> pre){
        List<String> filterList = new ArrayList<>();
        for(String s : list){
            if(pre.test(s)){
                filterList.add(s);
            }
        }
        return filterList;
    }

	@Test
    public void test2(){
        List<String> list = Arrays.asList("北京","东京","南京","天津","西京");
        System.out.println(filterString(list, new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return s.contains("京");
            }
        }));
//        Lambda写法
        System.out.println(filterString(list,s ->  s.contains("京") ));
    }

方法引用
  1. 使用情景:当要传递给Lambda体的操作已经有实现的方法了。可以使用方法引用
  2. 方法引用本质上就是Lambda表达式,而Lambda表达式作为函数式接口的实例,所以方法引用也是函数式接口的实例
  3. 使用格式: 类(或对象) : : 方法名
  4. 主要有以下三种情况
    1. 对象 : : 非静态方法
    2. 类 : : 静态方法
    3. 类 : : 非静态方法
  5. 要求:接口中的抽象方法方法引用的方法的形参列表和返回值类型相同(针对于情况1、2)

情况一:对象 : : 实例方法

//抽象方法:Consumer中的void accept(T t)
//引用方法:PrintStream中的void println(T t)    
Consumer<String> consumer = o-> System.out.println(o);
consumer.accept("北京");

PrintStream ps = System.out;
Consumer<String> consumer1 = ps :: println;
consumer1.accept("Beijing");

情况二:类 : : 静态方法

//抽象方法:Comparator中的int compare(T t1, T t2)
//引用方法:Integer中的int compare(T t1, T t2)
Comparator<Integer> com = (o1,o2)-> Integer.compare(o1,o2);
System.out.println(com.compare(12,21));

Comparator<Integer> com1 = Integer::compareTo;
System.out.println(com1.compare(12,21));

情况三 :类 : : 实例方法

//抽象方法:Comparator中的int compare(T t1, T t2)
//引用方法:String中的int t1.compareTo(t2)
Comparator<String> com1 = (s1,s2)->s1.compareTo(s2);
System.out.println(com1.compare("abc","abf"));

Comparator<String> com2 = String::compareTo;
System.out.println(com2.compare("abc","abg"));
//抽象方法:BiPredicate中的boolean test(T t1, T t2)
//引用方法:String中的int t1.equals(t2)
BiPredicate<String,String> pre1 = (s1,s2)->s1.equals(s2);
System.out.println(pre1.test("abc","abc"));

BiPredicate<String,String> pre2 = String::equals;
System.out.println(pre2.test("abc","abc"));
构造器引用

与方法引用类似,函数式接口的抽象方法和构造器的形参列表一致,抽象方法的返回值类型即为构造器所属的类的类型

//Supplier中的 T get()
        Supplier<Object> sup = new Supplier<Object>() {
            @Override
            public Object get() {
                return new Object();
            }
        };
        System.out.println(sup.get());

        Supplier<Object> sup1 = ()->new Object();
        System.out.println(sup1.get());
        
        Supplier<Object> sup2 = Object :: new;
        System.out.println(sup2.get());
//Function中的R apply(T t)
Function<String , String> func1 = new Function<String, String>() {
    @Override
    public String apply(String s) {
        return new String(s);
    }
};
System.out.println(func1.apply("abc"));

Function<String , String> func2 = (o)->new String(o);
System.out.println(func2.apply("bcd"));

Function<String,String> func3 = String :: new;
System.out.println(func3.apply("poi"));
数组引用

可以把数组看做是一个特殊的类,则写法与构造器引用类似

Function<Integer,String[]> func1 = new Function<Integer, String[]>() {
    @Override
    public String[] apply(Integer integer) {
        return new String[integer];
    }
};
System.out.println(Arrays.toString(func1.apply(5)));

Function<Integer,String[]> func2 = len->new String[len];
System.out.println(Arrays.toString(func2.apply(6)));

Function<Integer,String[]> func3 = String[] :: new;
System.out.println(Arrays.toString(func3.apply(7)));

13.3 Stream API

概述

Stream是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列

  1. Stream关注的是对数据的运算,与cpu相关

    集合关注的是数据的存储,与内存相关

  2. 注意点:

    1. Stream不会存储元素
    2. Stream不会改变源对象,相反,会返回一个持有结果的新Stream
    3. Stream操作是延迟执行的,会等到需要结果的时候才执行
  3. Stream执行流程

    1. Stream实例化
    2. 一些列的中间操作(过滤、映射…):一个中间操作链,对数据源的数据进行处理
    3. 终止操作:一旦执行终止操作,就执行中间操作链,并产生结果。之后不会再被使用

本节测试用到的Employee类

public class Employee {
    private int id;
    private String name;
    private int age;
    private double salary;
    public static List<Employee> getEmployess(){
        List<Employee> list = new ArrayList<>();
        list.add(new Employee(1001,"马化腾",24,2000));
        list.add(new Employee(1002,"马云",64,3000));
        list.add(new Employee(1003,"刘强东",18,4000));
        list.add(new Employee(1004,"雷军",24,5000));
        list.add(new Employee(1005,"李彦宏",36,6000));
        list.add(new Employee(1006,"比尔盖茨",31,7000));
        list.add(new Employee(1007,"任正非",34,8000));
        list.add(new Employee(1008,"扎克伯格",66,9000));
        return list;
    }
    public Employee() {
    }

    public Employee(int id, String name, int age, double salary) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    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 double getSalary() {
        return salary;
    }

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Employee employee = (Employee) o;

        if (id != employee.id) return false;
        if (age != employee.age) return false;
        if (Double.compare(employee.salary, salary) != 0) return false;
        return name != null ? name.equals(employee.name) : employee.name == null;
    }

    @Override
    public int hashCode() {
        int result;
        long temp;
        result = id;
        result = 31 * result + (name != null ? name.hashCode() : 0);
        result = 31 * result + age;
        temp = Double.doubleToLongBits(salary);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        return result;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", salary=" + salary +
                '}';
    }
}
实例化

创建Stream方式一:通过集合

       List<Employee> employess = Employee.getEmployess();

//        default Stream<E> stream() : 返回一个顺序流
        Stream<Employee> stream = employess.stream();

//        default Stream<E> parallelStream():返回一个并行流
        Stream<Employee> parallelStream = employess.parallelStream();

创建Stream方式二:通过数组

//  调用Arrays类的静态方法 static <T> Stream<T> stream(T[] array):返回一个流
    int[] arr = new int[]{1,2,3,4,5,6};
    IntStream stream1 = Arrays.stream(arr);

    Employee e1 = new Employee(1001, "tom", 23, 6000);
    Employee e2 = new Employee(1002, "jer", 22, 5000);
    Employee[] arr1 = new Employee[]{e1,e2};
    Stream<Employee> stream2 = Arrays.stream(arr1);

创建Stream方式三:通过Stream的of()

Stream<Integer> stream3 = Stream.of(1, 2, 3, 4, 5, 6);

创建Stream方式四:创建无限流

//迭代  
//public static<T> Stream<T> iterate(final T seed,final UnaryOperator<T> f)
Stream.iterate(0,i->i+2).limit(10).forEach(System.out::println);

// 生成  
//public static<T> Stream<T> generate(Supplier<T> s)   
Stream.generate(Math::random).limit(10).forEach(System.out::println);
中间操作

切片与筛选、映射、排序

1、筛选与切片

  1. filter(Predicate p)——接收Lambda,从流中排除某些元素

    练习:查询员工表中工资大于5000的并输出

Stream<Employee> stream = list.stream();
/*stream.filter(new Predicate<Employee>() {
    @Override
    public boolean test(Employee employee) {
        return employee.getSalary()>=5000;
    }
});*/
stream.filter(e -> e.getSalary() > 5000).forEach(System.out::println);
  1. limit(int n)——截断流,使其元素不超过给定数量n

    list.stream().limit(3).forEach(System.out::println);
    
  2. skip(int n)——跳过元素,返回一个跳过前n个元素的流,若不足n个则返回空流

    list.stream().skip(3).forEach(System.out::println);
    
  3. distinct()——筛选,通过流所生成元素的hashCode()和equals()去除重复元素

2、映射

  1. map(Function func)——接受一个函数作为参数,将元素转换成其他形式或提取信息,该函数会被应用到每个元素上,并将其映射成一个新的元素

    List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
    /*        list.stream().map(new Function<String, Object>() {
                @Override
                public Object apply(String s) {
                    return s.toUpperCase();
                }
            }).forEach(System.out::println);*/
    list.stream().map(str -> str.toUpperCase()).forEach(System.out::println);
    

    练习:输入员工姓名长度大于3的姓名

    List<Employee> list = Employee.getEmployess();
    Stream<String> nameStream = list.stream().map(Employee::getName);
    nameStream.filter(e->e.length()>3).forEach(System.out::println);
    
  2. flatMap(Function fun)——接受一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流,类似List a.addAll(List b)

    //将字符串转换成字符流
    public static Stream<Character> strToStream(String str){
        ArrayList<Character> list = new ArrayList<>();
        for(char c : str.toCharArray()){
            list.add(c);
        }
        return list.stream();
    }
    @Test
    public void test4(){
        List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
        list.stream().map(StreamTest1::strToStream).forEach(
                s -> s.forEach(System.out::println)
        );
        list.stream().flatMap(StreamTest1::strToStream).forEach(System.out::println);
    }
    

3、排序

List<Integer> list = Arrays.asList(1, 3, 5, 7, 9, 2, 4, 6, 8, 10);
  1. sorted()——自然排序

    list.stream().sorted().forEach( System.out::println);
    
  2. sorted(Comparator com)——定制排序

    list.stream().sorted(((o1, o2) -> o2-o1)).forEach(System.out::println);
    
终止操作

匹配与查找、规约、收集

1、匹配与查找

List<Employee> list = Employee.getEmployess();
  1. allMatch(Predicate p)——检查p是否匹配所有元素

    boolean allMatch = list.stream().allMatch(e -> e.getAge() > 18);
    
  2. anyMath(Predicate p)——检查p是否匹配至少一个元素

    boolean anyMatch = list.stream().anyMatch(e -> e.getAge() > 18);
    
  3. noneMath(Predicate p)——检查是否没有p匹配的元素

    boolean noneMatch = list.stream().noneMatch(e -> e.getName().startsWith("雷"));
    
  4. findFirst()——返回第一个元素

    Optional<Employee> first = list.stream().findFirst();
    
  5. findAny()——返回当前流中的任意元素

    Optional<Employee> any = list.parallelStream().findAny();
    
  6. count()——返回流中元素的总个数

    long count = list.stream().filter(e -> e.getSalary() > 5000).count();
    
  7. max(Comparator c)——返回流中最大值

    Stream<Double> salaryStream = list.stream().map(e -> e.getSalary());
    Optional<Double> max = salaryStream.max(Double::compare);
    
  8. min(Comparator c)——返回流中最小值

    Optional<Employee> min = list.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
    
  9. forEach(Consumer c)——内部迭代

    list.stream().forEach(System.out::println);
    
    //使用集合的遍历操作
    list.forEach(System.out::println);
    

2、规约

  1. reduce(T identity, BinaryOperator)——将流中元素反复结合,得到一个值,返回这个值
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Optional<Integer> reduce = list.stream().reduce(0, new BinaryOperator<Integer>() {
    @Override
    public Integer apply(Integer integer, Integer integer2) {
        return integer + integer2;
    }
});
//方法引用
Optional<Integer> reduce1 = list.stream().reduce(0, Integer::sum);
  1. reduce(BinaryOperator)——将流中元素反复结合,得到一个值,返回Optional< T >
List<Employee> list = Employee.getEmployess();
Optional<Double> totalSalary = list.stream().map(Employee::getSalary).reduce(Double::sum);

3、收集

collect(Collector c):将流转换为其他形式,接受一个Collector接口的实现,用于给Stream中元素做汇总

使用Collectors的方法可以获取Collector接口的实现

List<Employee> list = Employee.getEmployess();
List<Employee> employeeList = list.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList());
employeeList.forEach(System.out::println);

13.4 Optional类

Optional< T >类(java.util.Optional)是一个容器类,可以保存类型T的值,代表这个值存在;或者仅仅保存null,表示这个值不存在

原来用null表示一个值不存在,现在Optional可以更好的表达这个概念,并且可以避免空指针异

常用方法:ofNullable(T t)

​ orElse(T t)

Option类提供很多方法,这样就不用显式进行空值检测

我的学习笔记有更多精彩内容哦
Java编程知识专栏

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值