Java8新特性文章

Posted 童柏

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java8新特性文章相关的知识,希望对你有一定的参考价值。

Java8新特性

一、Lambda表达式

Lambda是一个匿名函数。我们可以把Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。

//原来的匿名内部类
public void test1(){
    Comparator<Integer> com =new Comparator<Integer>(){
        @Override
        public int compare(Integer o1,Integer o2){
            return Integer.compare(o1,o2);
        }
    };
    TreeSet<Integer> ts = new TreeSet<>(com);
}
//Lambda表达式
public void test2(){
    Comparator<Integer> com = (x,y)->Integer.compare(x,y);
    TreeSet<Integer> ts = new TreeSet<>(com);
}

1、Lambda表达式的基础语法

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

​ 左侧:Lambda表达式的参数列表。

​ 右侧:Lambda表达式中所需执行的功能,即为Lambda体。

//语法格式一:无参数,无返回值
()->System.ont.println("Hello");
//语法格式二:有一个参数,无返回值,参数的小括号可以不写
(x)->System.ont.println(x);
//语法格式三:有两个参数,有返回值,并且Lambda体中有多条语句
Comparator<Integer> com = (x,y)->{
    System.ont.println("函数式接口");
    return Integer.compare(x,y);
}
//语法格式四:两参数,有返回值,Lambda体只有一条语句,return和大括号都可以不写
Comparator<Integer> com = (x,y)->Integer.compare(x,y);
//语法格式五:Lambda表达式参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出数据类型,即“类型推断”。
(Integer x,Integer y)->Integer.compare(x,y);

2、Lambda表达式需要“函数式接口”支持

函数式接口:接口中只有一个抽象方法的接口,称为函数式接口。可以使用注解@FunctionalInterface修饰,可以检查是否是函数式接口。

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

函数式接口参数类型返回类型用途
Consumer<T><br/>消费型接口Tvoid对类型为T的对象应用操作,包含方法:<br/>void accept(T t);
Supplier<T><br/>供给型接口T返回类型为T的对象,包含方法:<br/>T get();
Function<T,R><br/>函数型接口TR对类型为T的对象应用操作,并返回结果。结果是R类型的对象。包含方法:<br/>R apply(T t);
Predicate<T><br/>断定(言)型接口Tboolean确定类型为T的对象是否满足某约束,并返回boolean值。包含方法:<br/>boolean test(T t);

3、方法引用

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

//主要有三种语法格式:
//对象::实例方法名
Supplier<Integer> sup2 = emp::getAge;
//类::静态方法名
Comparator<Integer> com = (x,y)->Integer.compare(x,y);//原写法
Comparator<Integer> com1 = Integer::compare;//现写法
//类::实例方法名
BiPredicate<String,String> bp = (x,y)->x.equals(y);//原写法
BiPredicate<String,String> bp2 = String::equals;
//注意:1.Lambda体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保持一致。
//2.若Lambda参数列表中的第一个参数是实例方法的调用者,而第二个参数是实例方法的参数时,可以使用ClassNmae::method。

4、构造器引用

//格式:ClassName::new
Supplier<Employee> sup = ()->new Employee();//原写法
Supplier<Employee> sup2 = Employee::new;//现写法
//注意:需要调用的构造器参数列表要与函数式接口中抽象方法的参数列表保持一致

5、数组引用

//格式:Type[]::new;
Function<Integer,String[]> fun = (x)->new String[x];//原写法
Function<Integer,String[]> fun2 = String[]::new;

二、流(Stream)

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

==集合讲的是数据,流讲的是计算!==

注意:
1.Stream自己不会存储元素。
2.Stream不会改变源对象。相反,他们会返回一个持有结果的新Stream。
3.Stream操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
Stream操作的三个步骤
1.创建Stream:一个数据源(如集合、数组),获取一个流。
2.中间操作:一个中间操作链,对数据源的数据进行处理。
3.终止操作(终端操作):一个终止操作,执行中间操作链,并产生结果。

1.创建Stream

//1.可以通过Collection系列集合提供的Stream()或paralleStream()
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("a","b","c");
//4.创建无限流
/*迭代:*/Stream<Integer> Stream4 = Stream.iterate(0/*种子,起始值*/,(x)->x+2);
/*生成:*/Stream.generate(()->Math.random()); 

2.中间操作

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

a.filter筛选和切片

//filter筛选和切片,接收Lambda,从流中排除某些元素。
employee.stream().filter((e)->e.getAge>35);
//内部迭代:迭代操作由StreamAPI完成
//外部迭代:由自己写,例:
Iterator<Employee> it = employees.iterator();
while(it.hasNext()){
    System.out.println(it.next());
}

b.limit截断流

//limit:截断流,使其元素不超过给定元素
employees.stream().filter((e)->e.getSalary>5000).limit(2);

c.skip跳过元素

//skip:跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流。与limit(n)互补。
employees.stream().filter((e)->e.getSalary>5000).skip(2);

d.distinct筛选

//distinct:筛选,通过流所生成元素的hashCode()和equals()去除重复元素。(重写hashCode和equals方法)
employees.stream().distinct();

e.map/flatMap映射

//map映射,接收Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
/*例1:*/list.stream().map((str)->str.toUpperCase());
/*例2:*/employees.stream().map(Employee::getName);

//flatMap——接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流都连接成一个流
list.stream().flatMap(TestStreamAPI2::filterCharacter);
/*把多个流变成一个流*//*参考list.add()和list.addAll()方式形成*/

f.sorted排序

//sorted排序
//自然排序(Comparable)——sorted()
list.stream().sorted();
//定制排序(Comparable)——sorted(Comparator com)
employees.stream().sorted((e1,e2)->{
    if(e1.getAge().equals(e2.getAge())){
        return e1.getName().compareTo(e2.getName());
    }else{
        return /*取反加负号,反着排序*/e1.getName().compareTo(e2.getName());
    }
})

g.查找与匹配

//查找与匹配
//allMatch()——检查是否匹配所有元素
boolean b1 = employees.stream().allMatch((e)->e.getStatus().equals(Status.Busy));
//anyMatch——检查是否至少匹配一个元素
boolean b2 = employees.stream().anyMatch((e)->e.getStatus().equals(Status.Busy));
//noneMatch——检查是否没有匹配所有元素
boolean b3 = employees.stream().nonEMatch((e)->e.getStatus().equals(Status.Busy));

h.返回第一个元素

//findFirst——返回第一个元素
Optional<Employee> op = employees.stream().sorted((e1,e2)->
    Double.compare(e1.getSalary(),e2.getSalary())).findFirst();

i.返回当前流中任意元素

//findAny——返回当前流中任意元素
Optional<Employee> op2 = employees.parallelStream().filter((e)->
    e.getStatus().equals(Status.Free)).findAny();

j.返回流中元素的总个数

//count——返回流中元素的总个数
Long count = employees.stream().count();

k.返回流中最大值

//max——返回流中最大值
Optional<Employee> op1 = employees.stream().max((e1,e2)->
    Double.compare(e1.getSalary(),e2.getSalary()));

l.返回流中最小值

//min——返回流中最小值
Optional<Employee> op2 = employees.stream().map(Employee::getSalary).min(Double::compare);

m.归约

//归约
//reduce(Tidentity,BinaryOperator)/reduce(BinaryOperator)——可以将流中元素反复结合起来,得到一个值。
/*例1:*/Integer sum = list.stream().reduce(0/*有初始值不为空,所以直接拿值*/,(x,y)->x+y);
/*例2:*//*无初始值,用Optional接*/Optional<Double> op = 
    employees.stream().map(Employee::getSalary).reduce(Double::sum);
//备注:map和reduce的连接通常称为map-reduce模式,因Google用它来进行网络搜索而出名。

3.终止操作

收集

//收集
//collect——将流转为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法。
List<String> list = employees.stream().map(Employee::getName).collect(Collectors.toList());
Set<String> set = employees.stream().map(Employee::getName).collect(Collectors.toSet());//去重
HashSet<String> hs = 
    employees.stream().map(Employee::getName).collect(Collectors.toCollection(HashSet::new));
//总数
Long count = employees.stream().collect(Collectors.counting());
//平均值
Double avg = employees.stream().collect(Collectors.averagingDouble(Employee::getSalary));
//总和
Double sum = employees.stream().collect(Collectors.summingDouble(Employee::getSalary));
//最大值
Optional<Employee> max = employees.stream().collect(Collectors.maxBy((e1,e2)->
    Double.compare(e1.getSalary(),e2.getSalary())));
//最小值
Optional<Double> min = 
    employees.stream().map(Employee::getSalary).collect(Collectors.minBy(Double::compare));
//分组
Map<Status,List<Employee>> map = 
    employees.stream().collect(Collectors.groupingBy(Employee::getStatus));
//多级分组
Map<Status,Map<String,List<Employee>>> map = employees.stream()
    .collect(Collectors.groupingBy(Employee::getStatus,
        Collectors.groupingBy((e)->{
            if(((Employee) e).getAge()<=35){
                return "青年";
            }else if(((Employee) e).getAge()<=50){
                return "中年";
            }else{
                return "老年";
            }
        })));
//分区(满足条件一个区,不满足条件一个区)
Map<boolean,List<Employee>> map = employees.stream()
    .collect(Collectors.partitioningBy((e)->e.getSalary()>8000));
//数据求和,求平均值,求最大值等另一种写法
DoubleSummaryStatistics dss = 
    employees.stream().collect(Collectors.summarizingDouble(Employee::getSalary));
/*求和:*/ dss.getSum(); /*求平均值*/ dss.getAverage(); /*求最大值*/ dss.getMax();
//字符串拼接(类似)
String str = employees.stream().map(Employee::getName).collect(Collectors.joining(",","===","==="));
/*(Joining括号里不写,则名字拼一起;写一个,则用那个分隔;写三个,则是分隔,字符串前,字符串后)*/

4.并行流与串行流

//并行流与串行流
/*“工作窃取”模式:当执行新的任务时,它可以将其拆分成更小的任务执行,并将小任务加到线程队列中,然后再从一个随机线程的队列中偷一个并把它放在自己的队列中(Fork/Join框架)*/
LongStream.rangeClosed(0,1000000000L).parallel().reduce(0,Long::sum);
//parallel()并行流 sequential()顺序流

5.Optional类

//Optional容器类的常用方法:
//1.Optional.of(T t):创建一个Optional实例
Optional<Employee> op = Optional.of(new Employee());
//2.Optional.empty():创建一个空的Optional实例
Optional<Employee> op = Optional.empty();
//3.Optional.ofNullable(T t):若t不为null,创建Optional实例,否则创建空实例
Optional<Employee> op = Optional.ofNullable(new Employee());
//4.isPresent():判断是否包含值
if(op.isPresent()){}
//5.orElse(T t):如果调用对象包含值,返回该值,否则返回t
Employee emp = op.orElse(new Employee());
//6.orElseGet(Supplier s):如果调用对象包含值,返回该值,否则返回s获取的值
Employee emp = op.orElseGet(()->new Employee());
//7.map(Function f):如果有值对其处理,并返回处理后的Optional,否则返回Optional.empty()
Optional<String> str = op.map((e)->e.getName());
//8.flatMap(Function mapper):与map类似,要求返回值必须是Optional
Optional<String> str2 = op.flatMap((e)->Optional.of(e.getName()));

三、接口中的默认方法

接口默认方法的“类优先”原则。

若一个接口中定义了一个默认方法,而另一个父类或接口中又定义了一个同名的方法时

  • 选择父类中的方法。如果一个父类提供了具体的实现,那么接口中具有相同名称和参数的默认方法会被忽略。
  • 接口冲突。如果一个父接口提供一个默认方法,而另一个接口也提供了一个具有相同名称和参数列表的方法(不管方法是否是默认方法),那么必须覆盖该方法来解决冲突。

    //接口中的默认方法和静态方法
    public interface MyInterface{//接口
        default String getName(){
            return "呵呵呵";
        }
        public static void show(){
            System.out.println("接口中的静态方法");
        }
    }

以上是关于Java8新特性文章的主要内容,如果未能解决你的问题,请参考以下文章

Java 8 新特性总结

Java8新特性终极指南

java8 新特性精心整理

Java8新特性一张图带你领略Java8有哪些新特性

java8 新特性精心整理(全)

java8 新特性精心整理(全)