java8相对于以前的版本来说,速度更快,数据结构和内存结构都做了修改。
一:快的缘由
数据结构方面:在hashMap中,底层的数据结构采用的是哈希表,当需要添加一个元素时,将key通过哈希算法计算得到哈希表索引值,根据索引值,找到对应位置,将该数据插入。在之前的版本中,如果key值不一致,则采用头插的链表插入方式,在java8中采用尾插的方式,并且,当哈希表使用比超过75%时,就进行扩容,之后将原来的数据按照插入时的方法重新计算并插入扩容后的哈希表中,当某链表长度超过8并且全部的长度超过64时,那么java会将原来的链表改为红黑树。这样除了插入数据,查询,删除等其他效率都很高。并且有一个线程安全的ConcurrentHashMap,在以前的版本中,有16个并发级别,对应16个段,每个段对应一个哈希链表。java8则去掉了16个段结构,直接采用CAS算法(无锁算法),并且采用了哈希链表和红黑树的数据结构。
内存结构方面:在以前版本中,java内存布局有栈,堆,在堆中有一个永久区,这个永久区中包含方法区,方法区用于加载类信息。在java中,将方法区分离出来,并且方法区中有一个MetaSpace元空间,使用的是物理内存,因此性能调优参数也由原来的PermGenSize和MaxPermGenSize变为MetaSpaceSize和MaxMetaSpaceSize。
二:Lamda表达式
2.1 实例
在以前的编程写法中,比如我们需要查询员工信息表中,(1)年龄大于25的;(2)薪资大于6000的,我们可以采用以下几种写法:
方式1:
(1)定义一个员工信息类,创建一个员工信息类的list,保存员工的姓名,年龄和薪资;
(2)定义一个方法,传入(1)中的list,遍历list,判断当前员工年龄是否大于25,若大于,则放进另一个作为返回值的list中,遍历完成,返回新的list。
(3)定义一个方法,传入(1)中的list,遍历list,判断当前员工薪资是否大于6000,若大于,则放进另一个作为返回值的list中,遍历完成,返回新的list。
(4)编写测试类,调用(2)和(3)中的方法,打印结果。
我们发现,(2)和(3)大致相同,只有判断条件不同,并且,我们需求改变,我们又需要按另一种条件查询,那么我们又得写一个类似(2)和(3)的方法。因此这种方式我们可以优化。
方式2(策略模式):
(1)写一个带泛型的接口,接口中定义一个方法;
(2)定义一个按年龄筛选的类,实现(1)中接口,重写方法,按照年龄判断
(3)要按薪资过滤,不需要动原来设计好的东西,只需要再创建一个按薪资过滤的类,实现接口方法。
(4)定义一个方法,方法参数1为员工信息list,参数2为接口类型的一个实例,在该方法中遍历list,调用接口中的实现类的重写方法;
(5)测试,调用(4)中方法,参数2传入实际按某条件查询的对应的(2)或者(3)类的对象实例。
这样如果增加了需求,只需要增加一个对应的实现类就可以了。
方式3:匿名内部类
就是将方式2中的(5)中调用(4)中方法的第二个参数直接传入一个匿名内部类,就new 一个接口实例,重写test方法就可以。当需求增加,再调用又重新定义匿名内部类,实现不同的方法实现。
实际上,我们可以发现,需求增加也就是判断条件改变以下就可以。
方式4:Lamda表达式
匿名内部类也不需要了,只需要在测试方法中,调用(4)中方式时第二个参数我们直接获取年龄或薪资进行条件判断。
当然还可以优化,方式5的基础是假设上述方法中的代码都不存在
方式5:Stream API
在测试方法中直接将员工信息的list转变为straem流,再打点按某条件过滤(.filter(这里参考lamda表达式)),想打印出结果的话,可以接着打点调用stream流中的foreach方法(.foreach(System.out::println))
2.2 Lamda语法
组成:
- lamda表达式操作符:->
- 参数列表:在操作符左侧,对应着接口抽象方法的参数列表
- lamda体:在操作符右侧,实际上就是最核心的部分,接口中抽象方法的实现的功能
Lamda表达式需要函数式接口的支持,函数式接口就是接口中只有一个抽象方法。可以使用@FunctionalInterface注解来修饰接口,该注解用于检查该接口是否为函数式接口。
Lamda表达式的基本规则:
- 无参数,无返回值:()-> System.out.println(“xxx”)
- 有一个参数,并且无返回值:(x)-> System.out.println(x)
- 有多个参数,并且Lamda体中有多条语句,并且有返回值:(x,y)-> {;;;return xx;}
如果有一个参数,那么左侧小括号可以不写,但习惯是写上;
如果lamda体有多条语句,则右侧必须写{};
如果lamda体中只有一条语句,打括号{}和return 都可以省略不写;
对于左侧的参数列表的数据类型,可以是省略不写,因为java的jvm编译器可以进行类型推断。但是一旦写类型,那么所有参数类型都必须写上。
口诀:左右遇一括号省,左侧推断类型省,右侧遇一返回省。
三:java四大核心内置函数式接口
(1). 消费型接口
Consumer<.T>
void accept(T t);
(2). 供给型接口
Supplier<.T>
T get();
(3). 函数型接口
Function<.T, R>
R apply(T t);
(4). 断言型接口
Predicate<.T>
boolean test(T t);