Java8新特性

笔记地址:https://blog.didispace.com/books/java8-tutorial/ch1.html

视频讲解:https://www.bilibili.com/video/BV1T54y1q7W2

Lambda与函数式接口

接口中有默认方法实现

Java 8 允许我们使用default关键字,为接口声明添加非抽象的方法实现。这个特性又被称为扩展方法

interface Formula {
    double calculate(int a);
    default double sqrt(int a) {
        return Math.sqrt(a);
    }
}

Lambda表达式

Java 8的一个大亮点是引入Lambda表达式,使用它设计的代码会更加简洁。通过Lambda表达式,可以替代我们以前经常写的匿名内部类来实现接口。

Lambda表达式本质是一个匿名函数,因为普通函数一般要先声明,然后定义返回值、方法名、参数列表、方法体,但是Lambda表达式只有参数列表和方法体

Lambda表达式组成

Lambda表达式由三部分组成:

  • 小括号,里面是参数列表
  • 箭头(goes to)
  • 花括号(方法体)

参数列表的参数类型可以省略,方法体中只有一行,花括号可以省略,如果只有一行return,也要省掉return关键字。只有一个参数,小括号也可以省略

初体验Lambda表达式

这个是我们以前的实现,匿名内部类,然后调用执行;

public class Demo {
    interface Test{
        int add(int a,int b);
    }
    public static void main(String[] args) {
        Test test = new Test() {
            @Override
            public int add(int a, int b) {
                return a + b;
            }
        };
        int c = test.add(1,2);
        System.out.println(c);
    }
}

我们现在用Lambda表达式改写下:

public class Demo {
    interface Test{
        int add(int a,int b);
    }
    public static void main(String[] args) {
        Test test = (a,b)->{
            return a+b;
        };
        int c = test.add(1,2);
        System.out.println(c);
    }
}

方法引用

语法:对象::方法,假如是static方法,可以直接类名::方法

@FunctionalInterface
interface Converter<F, T> {
    T convert(F from);
}

Converter<String, Integer> converter = Integer::valueOf;
Integer converted = converter.convert("123");
System.out.println(converted);    // 123

构造函数引用

如果函数式接口的实现恰好可以通过调用一个类的构造方法来实现,那么就可以使用构造方法引用

语法:类名::new

public class Program3 {
    public static void main(String[] args) {
        // 普通方式
        DogService dogService=()->{
            return new Dog();
        };
        dogService.getDog();
        // 简化方式
        DogService dogService2=()->new Dog();
        dogService2.getDog();
        // 构造方法引用
        DogService dogService3=Dog::new;
        dogService3.getDog();
        // 构造方法引用 有参
        DogService2 dogService21=Dog::new;
        dogService21.getDog("小米",11);
    }
}

综合案例

public class Demo {
    public static void main(String[] args) {
        List<Dog> list=new ArrayList<>();
        list.add(new Dog("aa",1));
        list.add(new Dog("bb",4));
        list.add(new Dog("cc",3));
        list.add(new Dog("dd",2));
        list.add(new Dog("ee",5));
        // 排序
        System.out.println("lambda集合排序");
        list.sort((o1,o2)->o1.getAge()-o2.getAge());
        System.out.println(list);
        // 遍历集合
        System.out.println("lambda遍历集合");
        list.forEach(System.out::println);
    }
}

函数式接口

@FunctionalInterface注解:这个注解是函数式接口注解,所谓的函数式接口,当然首先是一个接口,然后就是在这个接口里面只能有一个抽象方法。这种类型的接口也称为SAM(Single Abstract Method interfaces)接口。

不管加不加这个@FunctionalInterface注解,如果以函数式接口的形式使用,都是可以的,但如果你的接口中定义了第二个抽象方法的话,编译器会直接抛出异常。

函数式接口特点:

  • 接口有且仅有一个抽象方法
  • 允许定义静态方法
  • 允许定义默认方法
  • 允许java.lang.Object中的public方法

我理解的函数式接口是一个接口,定义一个方法,通过引入其他方法的引用,将这个功能进行实现

@FunctionalInterface
interface Converter<F, T> {
    T convert(F from);
}

Converter<String, Integer> converter = Integer::valueOf;
Integer converted = converter.convert("123");
System.out.println(converted);    // 123

系统内置的函数式接口

Java8的推出,是以Lambda重要特性,一起推出的,其中系统内置了一系列函数式接口。在JDK的java.util.function包下,有一系列的内置函数式接口:

Lambda作用域

  • 访问外部局部变量:只可读,不可写,编译时会隐式编译成final,不过访问的变量可以不加final
  • 访问成员变量和静态方法:可读可写
  • 访问默认接口方法:不可读,更不可用

Map的Lambda

正如前面已经提到的那样,map是不支持流操作的。而更新后的map现在则支持多种实用的新方法,来完成常规的任务。

public class Demo {
    public static void main(String[] args) {
        Map<Integer, String> map = new HashMap<>();
        for (int i = 0; i < 10; i++) {
            map.putIfAbsent(i, "val" + i);
        }
        
        //循环遍历打印,如果lambda只写一个参数默认为key
        map.forEach((id, val) -> System.out.println(val));
        
        //对 hashMap 中指定 key 的值进行重新计算,前提是该 key 存在于 hashMap 中
        //如果重新计算的值为null则自动剔除
        map.computeIfPresent(3, (num, val) -> val + num);//val3->val33
        map.computeIfPresent(9, (num, val) -> null);//val9->删除
        
        //对 hashMap 中指定 key 的值进行重新计算,如果不存在这个 key,则添加到 hasMap 中,如果存在则不进行操作
        map.computeIfAbsent(23, num -> "val" + num);//无->val23
        map.computeIfAbsent(3, num -> "bam"+num);//val33->val33

        //删除指定k、v,如果v和写的不一样就不会删除
        map.remove(3, "val3");
        System.out.println(map.get(3));
        map.remove(3, "val33");
        System.out.println(map.get(3));

        //如果有就获取,没有就返回默认值
        System.out.println(map.getOrDefault(42, "not found"));

        //合并数据,会先判断指定的 key 是否存在,如果不存在,则添加键值对到 hashMap 中,如果存在就进行操作。
        map.merge(9, "val9", (value, newValue) -> value.concat(newValue));
        map.merge(9, "concat", (value, newValue) -> value.concat(newValue));
    }
}

Java中的流

Stream流

Stream操作可以是中间操作,也可以是完结操作。完结操作会返回一个某种类型的值,而中间操作会返回流对象本身,并且你可以通过多次调用同一个流操作方法来将操作结果串起来。

Stream是在一个源的基础上创建出来的,例如java.util.Collection中的list或者set(map不能作为Stream的源)。Stream操作往往可以通过顺序或者并行两种方式来执行。

Java 8中的Collections类的功能已经有所增强,你可以之直接通过调用Collections.stream()或者Collection.parallelStream()方法来创建一个流对象。

public class Demo {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(2,4,5,5,5,1,3,6,7);
        Optional<Integer> reduce = list.stream()
                .filter(item -> item+1 > 3)//中间操作,做一个映射
                .map(item->item*10)//映射
                .sorted((num1, num2) -> num1 - num2)//升序
                .distinct()//去重
                .reduce((num1, num2) -> num1 + num2);//结束操作,经过上面处理的值求和
        System.out.println(reduce.get());
    }
}

结束的操作示例:

  • match:返回布尔值,有anyMatch、allMatch、noneMatch等操作
  • count:返回一个数值,用来标识当前流对象中包含的元素数量
  • reduce:定义一个方法,对元素依次进行重复操作,最终结果会放在一个Optional变量里返回

ParallelStream并行流

流操作可以是顺序的,也可以是并行的。顺序操作通过单线程执行,而并行操作则通过多线程执行。我们可以用并行流进行操作来提高运行效率,所有的代码段几乎都相同,唯一的不同就是把stream()改成了parallelStream(),来看一下顺序排序和并行排序的对比:

public class Demo {
    public static void main(String[] args) {
        int max = 1000000;
        List<String> values = new ArrayList<>(max);
        for (int i = 0; i < max; i++) {
            UUID uuid = UUID.randomUUID();
            values.add(uuid.toString());
        }
        //顺序排序
        long t0 = System.nanoTime();
        long count = values.stream().sorted().count();
        long t1 = System.nanoTime();
        long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);
        System.out.println(String.format("sequential sort took: %d ms", millis));
        /**
         * sequential sort took: 766 ms
         */
    }
}

并行排序能节约一半的时间:

public class Demo {
    public static void main(String[] args) {
        int max = 1000000;
        List<String> values = new ArrayList<>(max);
        for (int i = 0; i < max; i++) {
            UUID uuid = UUID.randomUUID();
            values.add(uuid.toString());
        }
        long t0 = System.nanoTime();
        long count = values.parallelStream().sorted().count();
        long t1 = System.nanoTime();
        long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);
        System.out.println(String.format("parallel sort took: %d ms", millis));
        /**
         * parallel sort took: 411 ms
         */
    }
}

Collectors

Collectors是一个工具类,是JDK预实现Collector的工具类,它内部提供了多种Collector,我们可以直接拿来使用,非常方便。

它提供了许多操作,让我们调用起来十分方便,例如:

  • toCollection:将流中的元素全部放置到一个集合中返回,这里使用Collection,泛指多种集合。
  • toList/toSet:流中的元素放置到一个列表集合/无序集set中去,默认为ArrayList和HashSet。
  • joining:将流中的元素全部以字符序列的方式连接到一起,可以指定连接符和结果的前后缀
  • mapping:对流中的每个元素进行映射,即类型转换,然后再将新元素以给定的Collector进行归纳
  • collectingAndThen:该方法是在归纳动作结束之后,对归纳的结果进行再处理
  • counting:该方法用于计数
  • minBy/maxBy:生成一个用于获取最小/最大值的Optional结果的Collector
  • summingInt/summingLong/summingDouble:生成一个用于求元素和的Collector,首先通过给定的mapper将元素转换类型,然后再求和
  • averagingInt/averagingLong/averagingDouble:生成一个用于求元素平均值的Collector,首选通过参数将元素转换为指定的类型。求平均值涉及到除法操作,结果一律为Double类型。
  • reducing:reducing方法有三个重载方法,其实是和Stream里的三个reduce方法对应的,二者是可以替换使用的,作用完全一致,也是对流中的元素做统计归纳作用。
  • groupingBy:这个方法是用于生成一个拥有分组功能的Collector,它也有三个重载方法
  • partitioningBy:将流中的元素按照给定的校验规则的结果分为两个部分,放到一个map中返回,map的键是Boolean类型,值为元素的列表List
  • toMap:根据给定的键生成器和值生成器生成的键和值保存到一个map中返回,键和值的生成都依赖于元素,可以指定出现重复键时的处理方案和保存结果的map。
  • summarizingInt/summarizingLong/summarizingDouble:这三个方法适用于汇总的,返回值分别是IntSummaryStatistics,LongSummaryStatistics,DoubleSummaryStatistics。在这些返回值中包含有流中元素的指定结果的数量、和、最大值、最小值、平均值。所有仅仅针对数值结果。
class Person {
    String name;
    String sex;
    Integer serlary;
    public Person(String name, String sex, Integer serlary) {
        this.name = name;
        this.sex = sex;
        this.serlary = serlary;
    }
}
public class Demo {
    public static void main(String[] args) {
        ArrayList<Person> list = new ArrayList<>();
        list.add(new Person("小红","女",1000));
        list.add(new Person("小芳","女",2000));
        list.add(new Person("小张","男",3000));
        list.add(new Person("小王","男",4000));
        Map<String, List<Person>> collect = list.stream().collect(Collectors.groupingBy(p -> p.sex));
        collect.forEach((k,v)-> {
            System.out.println(k);
            v.stream().forEach(p-> System.out.println(p.name));
        });
    }
}

https://www.jianshu.com/p/7eaa0969b424

合并集合/数组

class Person {
    String name;
    public String sex;
    Integer serlary;
    public Person(String name, String sex, Integer serlary) {
        this.name = name;
        this.sex = sex;
        this.serlary = serlary;
    }
}
public class Demo {
    public static void main(String[] args) {
        ArrayList<Person> list = new ArrayList<>();
        list.add(new Person("小红","女",1000));
        list.add(new Person("小芳","女",2000));
        list.add(new Person("小张","男",3000));
        list.add(new Person("小王","男",4000));
        ArrayList<Person> list2 = new ArrayList<>();
        list2.add(new Person("小红","女",1000));
        list2.add(new Person("小芳","女",2000));
        list2.add(new Person("小张","男",3000));
        list2.add(new Person("小王","男",4000));
        //如果是数组就用Arrays.stream的方法转化成流
        List<Person> collect = Stream.concat(list.stream(), list2.stream()).collect(Collectors.toList());
        System.out.println(collect);
    }
}

时间日期API

  • 和SimpleDateFormat相比, DateTimeFormatter是线程安全的
  • Instant的精确度更高,可以精确到纳秒级,但是Date只能到毫秒
  • Duration可以便捷得到时间段内的天数、小时数等
  • LocalDatetime能够快速地获取年、月、日、下一月等。
  • TemporalAdjusters类中包含许多常用的静态方法,避免自己编写工具类。

Nashron

Nashorn 一个 javascript 引擎,Nashorn JavaScript Engine 在 Java 15 已经不可用了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

笼中小夜莺

嘿嘿嘿,请用金钱尽情地蹂躏我吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值