java8新特性

java8新特性

主要内容(哔哩哔哩上的视频

1.Lambda 表达式

为什么使用Lambda表达式?

  • Lambda是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使java的语言表达能力得到了提升。

Lambda表达式的基础语法:java8中引入了一个新的操作符"->"该操作符称为箭头操作符或Lambda操作符,箭头操作符将Lambda表达式拆成两部分。

左侧:Lambda表达式的参数列表
右侧:Lambda表达式中所需执行的功能,即Lambda体。
所谓的Lambda表达式就是对接口的一个实现,左侧的参数列表对应的就是接口中的抽象方法的参数列表,右侧就是对接口抽象方法实现的一个功能。

什么是函数式接口?
这个接口中只有一个抽象方法的接口就叫函数式接口,可以使用注解@FunctionalInterface修饰,Lambda表达式需要函数式接口的支持。

Lambda表达式语法格式

语法格式一:无参数,无返回值。

"->"操作符左边是接口的参数,右边是接口中抽象方法的实现。
举例:

public class TestMain {
    @Test
    public void test1(){
        Runnable r = () -> {
            int sum = 0;
            for(int i=1;i<100;i++){
                sum += i;
            }
            System.out.println(sum);
        };
        Thread t1 = new Thread(r);
        t1.start();
    }
}

语法格式二:有一个参数,无返回值。
(x) -> System.out.println(x);

    @Test
    public void test2(){
        Consumer<String> con = (x) -> System.out.println(x);
        con.accept("sssxxxx");
    }

语法格式三:若只有一个参数,小括号可以忽略不写。
x -> System.out.println(x);

语法格式四:有两个以上的参数,有返回值,并且Lambda体中有多条语句。

	@Test
    public void test3(){
        Comparator<Integer> com = (x, y) ->{
            System.out.println("函数式接口");
            return Integer.compare(x,y);
        };
        int num = com.compare(10,11);
        System.out.println(num);
    }

语法格式五:有两个以上参数,有返回值,并且Lambda体中只有一条语句,return和大括号都可以省略不写。

	@Test
    public void test4(){
        Comparator<Integer> com = (x,y)->Integer.compare(x,y);
        Integer num = com.compare(10,11);
        System.out.println(num);
    }

语法格式六:Lambda表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出,数据类型,即"类型推断"。

(Integer x,Integer y) ->Integer.Compare(x,y);

自定义函数式接口并使用

//自定义一个函数式接口
@FunctionalInterface
public interface MyFun<T> {
    T getValue(T t);
}

//使用自定义的函数式接口
@Test
public void test5(){
    Integer num = operate(10,x->x*x);
    System.out.println(num);
}
private Integer operate(Integer num,MyFun<Integer> myFun){
    return myFun.getValue(num);
}

练习:

1.调用Collections.sort()方法,通过定制排序比较两个Employee(先按年龄比,年龄相同按姓名比),使用lambda作为参数传递。

@Data
public class Employee {
    private Integer id;
    private String name;
    private Integer age;
    private Double salary;
    public Employee(Integer id, String name, Integer age, Double salary) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.salary = salary;
    }
}
@Test
    public void test1(){
        Collections.sort(emps,(s1,s2)->{
            if(s1.getAge().equals(s2.getAge())){
                return s1.getName().compareTo(s2.getName());
            }else{
                return Integer.compare(s1.getAge(),s2.getAge());
            }
        });
        for(Employee e : emps){
            System.out.println(e);
        }
    }

2.①声明式函数接口,接口中声明抽象方法,public String getValue(String str);
②声明类TestLambda,类中编写方法使用接口作为参数,将一个字符串转换成大写,并作为方法的返回值。
③再将一个字符串的第二个和第四个索引位置进行截取子串。

@FunctionalInterface
public interface MyFunction<T> {
    T getValue(T str);
}
@Test
    public void test2(){
        String str1 = this.operate("adccc",x->x.toUpperCase());
        System.out.println(str1);
        System.out.println("==============================");
        String str2 = this.operate("abcdefg",x->x.substring(1,4));
        System.out.println(str2);
    }
    private String operate(String str,MyFunction<String> myFunction){
        return myFunction.getValue(str);
    }

3.①声明一个带两个泛型的函数式接口,泛型类型为<T,R> T为参数,R为返回值
②接口中声明对应抽象方法
③在TestLambda类中声明方法,使用接口作为参数,计算两个long型参数的和
④再计算两个long型参数的乘积。

@FunctionalInterface
public interface MyFunctionFirst<T,R> {
    R sum(T t1,T t2);
}
@Test
    public void test3(){
        Long sum = this.operate(34l,56l,(x,y)->x+y);
        System.out.println(sum);
        System.out.println("================");
        Long sum1 = this.operate(10L,10L,(x,y)->x*y);
        System.out.println(sum1);
    }
    private Long operate(Long t1,Long t2,MyFunctionFirst<Long,Long> my){
        return my.sum(t1,t2);
    }

2.函数式接口

java内置四大核心函数式接口

在这里插入图片描述

Consumer<T> : 消费型接口
	void accept(T t);
	
	@Test
    public void test5(){//消费型接口
        this.happy(10000.0,x-> System.out.println("一晚上消费"+x+"元"));
    }
    private void happy(Double money, Consumer<Double> con){
        con.accept(money);
    }

Supplier<T> : 供给型接口
	T get();
	
	@Test
    public void test6(){//供给型接口
        Supplier<Integer> su = ()-> (int) (Math.random()*100);
        System.out.println(su.get());
    }
	
Function<T,R> : 函数型接口
	R apply(T t);
	
	@Test
    public void test7(){//函数型接口
        Function<Long,Long> fun = (x) ->{int i =0;i=10;x = x+i;return x;};
        Long i = fun.apply(100L);
        System.out.println(i);
    }

Predicate<T> : 断言型接口
	boolean test(T t);
	
	@Test
    public void test8(){//断言型接口
        Predicate<Integer> pre = (x) -> x > 10;
        Boolean bo = pre.test(20);
        System.out.println(bo);
    }

3.方法引用与构造器引用

方法引用:若Lambda体中的内容有方法已经实现了,我们可以使用"方法引用"(可以理解为方法引用是Lambda表达式的另外一种表现方式)

主要有三种语法格式:

对象::实例方法名
类::静态方法名
类::实例方法名

4.Stream API

什么是Stream?
是数据渠道,用于操作数据源(集合、数组)所生成的元素序列。集合讲的是数据,流讲的是计算。
注意:
Stream 自己不会存储元素
Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

Stream的操作三个步骤

  • 创建Stream
    一个数据源(如:集合、数组),获取一个流
  • 中间操作
    一个中间操作链,对数据源的数据进行处理
  • 终止操作(终端操作)
    一个终止操作,执行中间操作链,并产生结果。

在这里插入图片描述

可以对集合或者数组中的数据进行操作,流在对集合或者数组中的数据进行操作的时候,类似于数据的传输,也得有个数据传输的管道,流在管道传输的过程中,会做一系列流水线式的中间操作,比如过滤、排序、切片。做完这些操作之后,原来的数据源是不会改变的,会产生一个新的流。

//创建Stream
    @Test
    public void test9(){
        //1.可以通过Collection系列提供的Stream()或parallelStream()
        List<String> list = new ArrayList<>();
        Stream<String> stream1 = list.stream();

        //2.通过Arrays中的静态方法stream()获取数组流
        Employee[] emps = new Employee[10];
        Stream<Employee> 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);
        stream4.limit(10).forEach(System.out::println);
        //生成
        Stream.generate(()->Math.random())
                .limit(5)
                .forEach(System.out::println);
    }

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

帅选与切片

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

List<Employee> emps = Arrays.asList(
            new Employee(101,"张三",18,9999.99),
            new Employee(102,"李四",59,6666.66),
            new Employee(103,"王五",28,3333.33),
            new Employee(104,"赵六",8,7777.77),
            new Employee(105,"田七",38,5555.55)
    );
    //内部迭代: 迭代操作由Stream API完成
    @Test
    public void test10(){
        //中间操作,不会执行任何操作
        Stream<Employee> stream = emps.stream().filter(s-> {
            System.out.println("Stream API 的中间操作");
            return s.getAge()>35;
        });
        //终止操作,一次性执行全部内容,即"惰性求值"
        stream.forEach(System.out::println);
    }
    //以下是打印情况,当没有终止操作的时候,是不会有打印结果的,当有终止操作时,才会一次性执行全部内容。
    Stream API 的中间操作
	Stream API 的中间操作
	Employee(id=102, name=李四, age=59, salary=6666.66)
	Stream API 的中间操作
	Stream API 的中间操作
	Stream API 的中间操作
	Employee(id=105, name=田七, age=38, salary=5555.55)

distinct() 帅选,通过流所生成元素的hashCode()和equals()去除重复元素。

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

//短路 可以在某个方面提高效率 并不是遍历所有 而是发现两个的话就返回两个
    @Test 
    public void test11(){
        emps.stream().filter(e->{
            System.out.println("短路!");
            return e.getSalary()>5000;
        }).limit(2)
          .forEach(System.out::println);
    }

skip(long n) 跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit(n)互补。

映射

map(Function f) 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素

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

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

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

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

	@Test
    public void test12(){
        List<String> list = Arrays.asList("aaa","bbb","ccc","ddd","eee");
        Stream<Character> stream = list.stream().flatMap(e->filterCharacter(e));
        stream.forEach(System.out::print);
    }
    public static Stream<Character> filterCharacter(String str){
        List<Character> list = new ArrayList<>();
        for(Character ch : str.toCharArray()){
            list.add(ch);
        }
        return list.stream();
    }

排序

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

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

allMatch 检查是否匹配所有元素

anyMatch 检查是否至少匹配一个元素

noneMatch 检查是否没有匹配所有元素

findFirst 返回第一个元素

findAny 返回当前流中的任意元素

count 返回流中元素的个数

max 返回流中最大的值

min 返回流中最小值

List<Employee> emps = Arrays.asList(
            new Employee(101,"张三",18,9999.99, Employee.State.BUSY),
            new Employee(102,"李四",59,6666.66, Employee.State.FREE),
            new Employee(103,"王五",28,3333.33,Employee.State.FREE),
            new Employee(104,"赵六",8,7777.77,Employee.State.VOCATION),
            new Employee(105,"田七",38,5555.55,Employee.State.VOCATION)
    );
    @Test
    public void test13(){
        boolean b = emps.stream().allMatch(e->e.getState().equals(Employee.State.BUSY));
        System.out.println(b);
        boolean b1 = emps.stream().anyMatch(e->e.getState().equals(Employee.State.BUSY));
        System.out.println(b1);
        boolean b2 = emps.stream().noneMatch(e->e.getState().equals(Employee.State.BUSY));
        System.out.println(b2);
        Optional<Employee> optional = emps.stream().sorted((e1, e2)->e1.getSalary().compareTo(e2.getSalary())).findFirst();
        System.out.println(optional.get());
        Optional<Employee> op = emps.stream().filter(e->e.getSalary()>5000).findAny();
        System.out.println(op.get());
        Long count = emps.stream().count();
        System.out.println(count);
        Optional<Employee> e = emps.stream().max((e1,e2)->e1.getSalary().compareTo(e2.getSalary()));
        System.out.println(e.get());
        Optional<Employee> op1 = emps.stream().min((e1,e2)->e1.getAge().compareTo(e2.getAge()));
        System.out.println(op1.get());
    }

归约
reduce(T identity,BinaryOperator) 可以将流中元素反复结合起来,得到一个值。
reduce(BinaryOperator) 可以将流中元素反复结合起来,得到一个值。返回Optional

@Test
    public void test14(){
        Integer sum = emps.stream().map(e->e.getAge()).reduce(0,(x,y)-> x+y);
        System.out.println(sum);

        Optional<Double> dsum = emps.stream().map(e->e.getSalary()).reduce(Double::sum);
        System.out.println(dsum.get());

    }

备注:map和reduce的连接通常称为map-reduce模式,因Google用它进行网络搜索而出名。

收集
collect(Collector c) 将流转化为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法。
Collector接口方法的实现决定了如何对流执行收集操作(如收集到List、Set、Map)。但是Collectors实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下表

@Test
    public void test15(){
        //总数
        Long count = emps.stream().collect(Collectors.counting());
        System.out.println(count);
        //平均值
        Double avg = emps.stream().collect(Collectors.averagingDouble(Employee::getSalary));
        System.out.println(avg);
        //总和
        Double sum = emps.stream().collect(Collectors.summingDouble(Employee::getSalary));
        System.out.println(sum);
        //最大值
        Optional<Employee> op = emps.stream().collect(Collectors.maxBy((x1,x2)->x1.getSalary().compareTo(x2.getSalary())));
        System.out.println(op.get());
        //工资的最小值
        Optional<Double> minOp = emps.stream().map(Employee::getSalary).collect(Collectors.minBy(Double::compareTo));
        System.out.println(minOp.get());
        //分组
        Map<Employee.State,List<Employee>> map = emps.stream().collect(Collectors.groupingBy(Employee::getState));
        System.out.println(map);
        //多级分组(先按状态分组,再按年龄段分组,可以无线分下去)
        Map<Employee.State,Map<String,List<Employee>>> map1 = emps.stream().collect(Collectors.groupingBy(Employee::getState,Collectors.groupingBy(e->{
            if(e.getAge()<=35){
                return "青年";
            }else if(e.getAge() <=50){
                return "青年";
            }else{
                return "老年";
            }
        })));
        System.out.println(map1);
        //分区
        Map<Boolean,List<Employee>> map2= emps.stream().collect(Collectors.partitioningBy(e->e.getSalary()>10000));
        System.out.println(map2);
        //获取组函数(统计函数)
        DoubleSummaryStatistics statistics = emps.stream().collect(Collectors.summarizingDouble(Employee::getSalary));
        //获取平均值
        Double avg1 = statistics.getAverage();
        //获取个数
        long count1 = statistics.getCount();
        //获取最大值
        statistics.getMax();
        //获取最小值
        statistics.getMin();
        //获取总值
        statistics.getSum();
        //连接字符串
        String joinStr = emps.stream().map(Employee::getName).collect(Collectors.joining("-"));
    }

并行流与顺序流
并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。
Java8 中将并行进行了优化,我们可以很容易的对数据进行并行操作。Stream API 可以声明性地通过parallel()与sequential()在并行流与顺序流之间进行切换。

Fork/join框架与传统线程池的区别
采用"工作窃取"模式:
当执行新的任务时它可以将其拆分分成更小的任务执行,并将小任务加到线程队列中,然后再从一个随机线程队列中偷一个并把它放在自己的队列中。

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

Optional类
Optional类是一个容器类,代表一个值存在或不存在,原来用null表示一个值不存在,现在Optional可以更好的表达这个概念。并且可以避免空指针异常。
常用方法:
Optional.of(T t) :创建一个Optional实例
Optional.empty():创建一个空的Optional实例
Optional.ofNullable(T t):若t 不为null,创建Optional实例,否则创建空实例
isPresent() : 判断是否包含值
orElse(T t):如果调用对象包含值,返回该值,否则返回t
orElseGet(Supplier s) 如果调用对象包含值,返回该值 ,否则返回s获取的值
map(Function f) 如果有值对其处理,并返回处理后的Optional,否则返回Optional.empty()
flatMap(Function mapper) 与map类似,要求返回值必须是Optional。

5.接口中的默认方法与静态方法

6.新时间日期API

7.其他新特性

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值