Java 8 新特性

主要内容

  1. * Lambda表达式
  2. 函数式接口
  3. 方法引用与构造器引用
  4. * Stream API
  5. 接口中的默认方法与静态方法
  6. 新时间日期API
  7. 其他新特性

简介

  • 速度更快
  • 代码更少(增加了新的语法Lambda表达式)
  • 强大的StremAPI
  • 便于并行
  • 最大化减少空指针异常Optional

1.速度更快

a. 分析怎么速度更快的?
对底层数据结构最核心的一个改变就是HashMap,它对HashMap做了一个怎样的改动呢?
回顾:
  原来的HashMap是怎样的?为什么采用Hash表?如果说HashMap不采用Hash表的话,那么它里面存储的元素都是无序的,要往里面添加一个元素不能重复的话,比较两个元素是否一样要使用equals方法;如果不用哈希表和哈希算法就得用equals,那么把一个元素添加到HashMap集合中去就得跟每个元素都equals一次,假如说,集合中有一万个元素,那么添加一个元素得equals一万次,所以说,HashMap不使用哈希算法的话插入元素效率极低。
  因此,Sun公司采用的一种哈希表,或者说一种哈希算法,我们称它为哈希表,实际上在底层就是一个数组;这个数组中存的什么东西?存的是一个个的Entry。那么这个哈希表有什么好处呢?我们说数组都有索引值,Hash表默认的大小是多少呢?默认是16。
我们要往Hash表中添加一个对象的话,首先会调用这个对象的hashcode()方法,然后根据哈希算法对这个hashcode进行运算,运算之后生成数组的索引值,然后根据索引值找到对应的位置,首先看这个位置有没有对象存在,如果没有对象存在的话,就直接存储;但有可能对象生成的哈希码值一样,即如果有对象存在的话,就得再通过equals比较对象的内容value,如果说value值一样就直接更新;如果不一样,就形成链表:后加入的对象放链表前边,发生的这个情况我们称它为碰撞,那么这个情况是不是应该我们尽可能的去避免的。

  为什么说要尽可能的去避免呢?一旦这里碰撞的元素过多的话,会造成什么样的结果?碰撞的对象都要equals一次造成执行效率低,那么我们怎么去避免呢?hashcode方法和equals方法尽可能的写得严谨一些,并且尽可能的让hashcode方法和equals方法保持一致,即对象一样生成的hashcode值一样,对象的内容一样equals方法要返回为true;但是呢,即便我们的hashcode方法、equals方法写得再严谨一点,这种碰撞的情况我们也是避免不了的,只能说让它的碰撞尽可能减少,但是也避免不了碰撞。
怎么避免不了碰撞呢?当我们要把一个对象添加到HashMap中去的时候,调用哈希方法产生一个哈希码值,然后采用哈希算法把哈希码值运算成Entry[]数组个索引值;那么不管产生多少个哈希码值和什么哈希算法,但是数组索引值只能有一个,如果说这个数组的索引值有5个:0、1、2、3、4,那么这个对象产生的索引值一定在这5个索引值范围内;那么有可能存储10个对象,这个10个对象经过哈希码值运算以后,生成的数组索引值有可能一样;所以说碰撞这种情况我们是避免不了的,即便是我们hashcode方法写得再好,equals方法写得再好,碰撞也是避免不了的。
  既然避免不了,我们能让碰撞链表太长吗?肯定也不行。那是HashMap又是怎么做的呢?HashMap提供了一个加载因子,默认是0.75,0.75指的是什么呢?指的是当元素大小达到现有Hash表的75%时进行扩容,那么说可以到100%再进行扩容吗?绝对不能,有可能对象经过哈希码值运算后,有可能只存储某些索引值上,例如:有可能对象只存储在了1、3索引,0、2、4没有对象存储,所以说它不能达到100%之后再扩容。加载因子可以设太小吗?太小了也不行浪费空间,所以说加载因子就设置为哈希表容量的75%就扩容。
注意一旦扩容之后,就得把链表里面的每一个元素重新进行运算,生成成新的经过哈希算法运算的索引位置,值得注意的是每个链表元素都得重新算一下;那么这样的话,在某种程度上碰撞的概率就变低了,碰撞里边的内容就少了,这就是原来解决碰撞的方式。

  刚才我们也说了,即便是有这种扩容的机制,扩容之后这种碰撞还是避免不了,一旦避免不了那么意味着什么呢?意味着效率低,为什么效率低呢?比如说:现在我要查询一个对象,极端的情况下查询的可能是碰撞链表中最后一个元素,那么需要碰撞链表中每个元素都需要equals一次。

  在JDK1.8之后,对HashMap进行了一个改变:就不再是原来的数组+链表结构了,在数组+链表的基础上多了一个红黑树,红黑树是二叉树的一种;那么什么时候能变红黑树呢?如果这个链表碰撞的元素个数大于8时,与此同时,并且HashMap总容量大小大于64时,这时,会把这个链表转成红黑树;转成这种红黑树好在哪?除了添加以外,其他的效率都比链表快。如果是链表直接在添加到链表末尾就完事了,但是,如果说是红黑树添加的话,要比较二叉树节点大小后添加;查询时比较元素次数少了很多,所以说除了添加操作以外,其它操作效率都高了。而且HashMap容量扩容以后,不需要重新运算hashcode值,不需要通过哈希算法重新运算红黑树元素的索引值了,只需要找它对应元素的2倍,它对应元素就是:在原来Hash表的总长度+它当前所在的位置;例如:原来Hash表总长度为5时,它对应的元素就是5+3=8,所以说,不知不觉效率肯定提高了。HashMap变了,那么HashSet变不变?那肯定也变了呀,而且Hash表的方式相对来说应用的也是非常广泛的,未来在工作中不知道选哪种数据结构的时候就选Hash表。

  HashMap变了,HashSet也变了,那么ConcurrentHashMap也进行升级了;首先,它变了什么呢?在JDK1.7时它有一个并发级别,并发级别默认是多少?concurrentLevel默认是16,采用的锁分段机制,默认是16个段,每一个段里面对应一个表,这个表也是默认16;在JDK1.8之后,这个锁分段机制就几乎没有用了,改成了CAS算法(无锁算法),段为啥取消呢?这个段的长度不好评定,并发级别太大它很多的空间就浪费了,如果太小会操作它每一段里面的元素过多,那么它效率也低;所以之后它都采用这种哈希算法,采用哈希算法后,它里面有一个哈希表,哈希表也变成了数组-链表-红黑树,这样的话,它效率也会提升,并且它采用了CAS算法,它比锁效率要高,它是底层操作系统支持的操作算法,所以说ConcurrentHashMap效率也提高了。

b. 对底层内存结构也变得不一样了

内存图:栈、堆、方法区
方法区实际上是属于堆中永久区的一部分,堆中除永久区以外成为垃圾回收区;既然方法区属于堆中永久区的一部分,那么每次画内存图时为什么把它画到单独一块,永久区都存一些什么?主要时加载一些类信息,或者说一些核心类库都加载在这,那么永久区的内容会不会被垃圾回收机制所回收呢?几乎不会,虽然叫永久区,但是,不是说它不会被垃圾回收机制回收,它是会被垃圾回收机制所回收的,只不过回收的条件比较苛刻;既然方法区是永久区的一部分,那我们为什么把它画到外边来呢?实际上,JVM厂商有很多种:

Oracle-SUN Hotspot
Oracle JRocket
IBM J9 JVM
Taobao JVM
早在JDK1.7以前的时候,除了Oracle-SUN Hotspot以外,其它的JVM厂商早就没有永久区了,它把方法区从永久区中剥离出来,那么也就是说Hotspot也逐渐把方法区从永久区中剥离出来,所以说,我们平时作图就不会把它画在堆中。
于是,在JDK1.8以后,这个永久区就被彻底的干掉了,那么以后就没有这个永久区了。没有永久区之后,取而代之的叫Metaspace元空间,它相较原来的方法区有什么不同呢?最大的不同之处就在于什么呢?它是使用的物理内存;既然类的加载信息都存元空间了,那么OOM(Out Of Memory)异常会不会发生?也会,只不过发生的概率极低,因为它现在使用的是物理内存,要想把物理内存充破那还是挺难的,除非这个应用程序专门用于加载Class字节码文件的,把一千万个或一亿个Class都加载到内存中来,那么就有可能导致OOM。

既然永久区这边的内存空间都变了,相应的之前JVM调优的参数也变了,其中有一个PremGenSize参数已经无效了,相应的MaxPremGenSize也没有了,在JDK1.8中都没有了,取而代之的就是:MetaSpaceSize、MaxMetaSpaceSize;默认的就是物理空间多大,元空间就用多大,当然你也可以配置。

1.Lambda表达式

1.1.为什么使用Lambda表达式

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

1.2.Lambda案例

a. Employee实体类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Employee {

    private String name;
    private Integer age;
    private Double salary;
}

b. Lambda测试代码

public class TestLambda {

    // 原来的匿名内部类
    @Test
    public void test1() {
        Comparator<Integer> comparator = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return Integer.compare(o1, o2);
            }
        };
        TreeSet<Integer> treeSet = new TreeSet<>(comparator);
    }

    // Lambda表达式
    @Test
    public void test2() {
        Comparator<Integer> comparator = (x, y) -> Integer.compare(x, y);
        TreeSet<Integer> treeSet = new TreeSet<>(comparator);
    }

    List<Employee> employees = Arrays.asList(
            new Employee("张三", 18, 9999.99),
            new Employee("李四", 38, 5555.99),
            new Employee("王五", 50, 6666.66),
            new Employee("赵六", 16, 3333.33),
            new Employee("田七", 8, 7777.77)
    );

    // 需求:获取当前公司中员工年龄大于35的员工信息
    @Test
    public void test3() {
        List<Employee> list = filterEmployee(this.employees);
        for (Employee employee : list) {
            System.out.println(employee);
        }
    }

    public List<Employee> filterEmployee(List<Employee> list) {
        List<Employee> emps = new ArrayList<>();
        for (Employee emp : list) {
            if (emp.getAge() >= 35) {
                emps.add(emp);
            }
        }
        return emps;
    }

    // 需求:获取当前公司中员工工资大于5000的员工信息
    public List<Employee> filterEmployee2(List<Employee> list) {
        List<Employee> emps = new ArrayList<>();
        for (Employee emp : list) {
            if (emp.getSalary() >= 5000) {
                emps.add(emp);
            }
        }
        return emps;
    }

    // 优化方式一: 策略设计模式
    @Test
    public void test4() {
        List<Employee> list = filterEmployee(employees, new FilterEmployeeByAge());
        for (Employee employee : list) {
            System.out.println(employee);
        }

        System.out.println("-------------------");

        List<Employee> list2 = filterEmployee(employees, new FilterEmployeeBySalary());
        for (Employee employee : list2) {
            System.out.println(employee);
        }
    }

    public List<Employee> filterEmployee(List<Employee> list, MyPredicate<Employee> myPredicate) {
        List<Employee> emps = new ArrayList<>();
        for (Employee employee : list) {
            if (myPredicate.test(employee)) {
                emps.add(employee);
            }
        }
        return emps;
    }

    // 优化方式二:匿名内部类
    @Test
    public void test5() {
        List<Employee> list = filterEmployee(this.employees, new MyPredicate<Employee>() {
            @Override
            public boolean test(Employee employee) {
                return employee.getSalary() <= 5000;
            }
        });
        for (Employee employee : list) {
            System.out.println(employee);
        }

    }

    // 优化方式三:Lambda表达式
    @Test
    public void test6() {
        List<Employee> list = filterEmployee(this.employees, e -> e.getSalary() >= 5000);
        list.forEach(System.out::println);
    }

    // 优化方式四:Stream API
    @Test
    public void test7() {
        employees.stream()
                .filter(e -> e.getSalary() >= 5000)
                .limit(2)
                .forEach(System.out::println);

        System.out.println("-------------------");

        employees.stream()
                .map(Employee::getName)
                .forEach(System.out::println);
    }
}

c. 附:接口/策略类
MyPredicate接口

public interface MyPredicate<T> {

    public boolean test(T t);
}

策略类

public class FilterEmployeeByAge implements MyPredicate<Employee> {
    @Override
    public boolean test(Employee employee) {
        return employee.getAge() >= 35;
    }
}
public class FilterEmployeeBySalary implements MyPredicate<Employee> {
    @Override
    public boolean test(Employee employee) {
        return employee.getSalary() >= 5000;
    }
}
posted @   冰枫丶  阅读(79)  评论(0编辑  收藏  举报
编辑推荐:
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示