java8新特性
Posted strandtrack
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java8新特性相关的知识,希望对你有一定的参考价值。
一
1.新特性主要内容:
- Lambda 表达式
- 函数式接口
- 方法引用 / 构造器引用
- Stream API
- 接口中的默认方法 / 静态方法
- 新时间日期 API
- 其他新特性
2.新特性作用:
- 速度更快:
hashMap的改动:
jdk1.7的hashMap:
就是hash表,我们称它为hash表,但是底层其实就是数组,这个数组中存的都是entry。用这种数组有什么好处呢?我们知道数组都得有索引值,比如0,1,2,3,4。当新添加一个值时,通过hash算法算出数组的索引值,根据索引值找到数组的位置。看这个位置是否有对象在,如果没有对象在就直接存储。如果有对象再通过equals比较两个对象的内容,如果内容一样它的value就把以前的value覆盖掉了,等于还是保存的一个对象。如果内容不一样那就形成链表,后加的放前边做表头,这种情况称他为碰撞,这种情况我门应该尽量避免。为什么要避免呢,因为如果这里的元素过多,那就会效率低。因为如果我再加一个对象的话,加入这里我需要跟这个索引里的对象都进行比较内容,这就效率很低了。碰撞是不可避免地,因为你的数组索引值就那么些,不管你的hash算法怎么做,他最终对应的还是那几个数组索引值。但是我们可以避免里面的元素过多,这时候java开发者就提出了叫加载因子的东西,加载因子默认是0.75。0.75指的是当元素到达hash表的75%的时候进行扩容。为什么不到100%扩容?因为我可能hash算法算出的索引只有123,0和4都没有对象进去,那我永远到达不了100。一旦扩容的话,他就会吧链表里面的内容重排序,运算成新的位置,比如我这个对象本来在索引1里,重新运算他就到5里了。这样的话,碰撞的概率就降低了,这就是java7的方式。
java8的hashMap:
即使是有扩容的机制,还是避免不了碰撞,这样效率还是低,于是在java8之后又做了改变。以前是数组+链表,现在是数组+链表+红黑树。什么时候变红黑树呢?当你碰撞的元素个数大于8,并且在总元素(或者叫总容量)大于64,就把链表转变为红黑树。转变为红黑树的好处:除了添加以外其余的效率都比链表高。这里进行扩容也不需要重新排序,只需要把以前数组总索引+当前索引就行,加入我现在的索引是3,5(01234总索引是5)+3=8,新的位置就是8。这里的原理就是红黑树的原理,也就是二叉树,左小右大,比当前元素小的元素放左边,在小继续放。这里就是红黑树的知识了,可以自己看看,这里不再赘述,只是简单说一下。
hashSet也是同理。
java7的ConcurrentHashMap:
并发级别是16,就是默认分成16个段,每个段里面又分16张表。
java8的ConcurrentHashMap:
在java8之后这个段就不用了,改为了CAS算法。为什么取消呢?因为有可能段太大,那就浪费了。可是如果太小了就造成每个段中元素过大,效率过低。现在就改成了链表+红黑树,并且还采用了CAS算法,他要比锁的效率高(cas是并发的算法,无锁比较,比较之前的值和现在的值是不是期望值)。所以ConcurrentHashMap效率也提高了。
java7的内存结构:
有栈,堆和方法区(PremGen),还有一部分垃圾回收区。方法区是属于堆的永久区的一部分,永久区主要存的就是一些类信息,永久区几乎不被垃圾回收机制所回收,但也有例外,比如说你方法区快要满的时候,那就会进行回收。
java8的内存结构:
没有永久区了,取而代之的是MetaSpace,也就是元空间,方法区也从永久区剥离出来了。元空间和永久区的最大的不同就是元空间使用的是物理内存,也就是说直接用的是你系统的内存,而不是自己弄的内存,也就意味着垃圾回收机制也优化了,当元空间快要满的时候,或者说容量过大的时候垃圾回收机制才会开始回收,而现在我们用的是物理内存,辣么大,所以回收的几率就会很低。PremGenSize和MaxPremGenSize参数也无效了,取而代之的是MetaSpaceSize和MaxMetaSpaceSize。
- 代码更少
- 强大的 Stream API
- 便于并行
- 最大化减少空指针异常 Optional (Kotlin ?)
二
1.lambda表达式(重点)
简介: Lambda是一个匿名函数,可以理解为一段可以传递的代码(将代码像数据一样传递);可以写出更简洁、更灵活的代码;作为一种更紧凑的代码风格,是Java语言表达能力得到提升。
匿名内部类和lamda表达式的比较(实现同样的功能):
@Test
public void test(){
//匿名内部类
Comparator<Integer> comparator = new Comparator<Integer>() {
@Override
public int compare(Integer a1, Integer a2) {
return Integer.compare(a1,a2);
}
@Override
public boolean equals(Object obj) {
return false;
}
};
//调用
TreeSet<Integer> set = new TreeSet<>(comparator);
}
@Test
public void test2(){
// Lambda 表达式
Comparator<Integer> comparator = (a, b) -> Integer.compare(a, b);
TreeSet<Integer> set = new TreeSet<>(comparator);
}
函数式接口:
- 接口中只有一个抽象方法的接口,称为函数式接口,如:
/**
* 函数式接口
*/
public interface MyPredicte<T> {
public boolean test(T t);
}
可以使用注解 @FunctionalInterface
修饰的,则为函数式接口
/**
* 接口,用于解决重复条件
* @param <T>
*/
@FunctionalInterface
public interface MyPredicte<T> {
public boolean test(T t);
}
只有函数式接口时,lambda表达式才能生效
语法格式一:
无参数,无返回值,如() -> sout
实例:实现Runnable接口
语法格式二:
有一个参数,并且无返回值
(x)->System.out.println(x);
语法格式三:
若只有一个参数,小括号可以省略不写
x->System.out.println(x)
语法格式四:
有两个以上的参数,有返回值,并且lambda表达式中有多条语句
Comparator<Integer> com =(x,y)->{
System.out.println("凑个语句");
return Integer.compare(x,y);
};
语法格式五:
若lambda体中只有一条语句,return和大括号可以省略不写
Comparator<Integer> com=(x,y)->return Integer.compare(x,y);
语法格式六:
lambda表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出数据类型,即类型推断
三 函数式接口
Java内置四大核心函数式接口:
函数式接口 | 参数类型 | 返回类型 | 用途 |
Consumer 消费型接口 | T | void | 对类型为T的对象应用操作:void accept(T t) |
Supplier 提供型接口 | 无 | T | 返回类型为T的对象:T get() |
Function<T, R> 函数型接口 | T | R | 对类型为T的对象应用操作,并返回结果为R类型的对象:R apply(T t) |
Predicate 断言型接口 | T | boolean | 确定类型为T的对象是否满足某约束,并返回boolean值:boolean test(T t) |
四 引用:
1.方法引用:
若 Lambda 表达式体中的内容已有方法实现,则我们可以使用“方法引用”,就是相当于对lambda的简化。
语法格式:
- 对象 :: 实例方法
- 类 :: 静态方法
- 类 :: 实例方法
对象::实例方法
@Test
public void test01(){
PrintStream ps = System.out;
Consumer<String> con1 = (s) -> ps.println(s);
con1.accept("aaa");
Consumer<String> con2 = ps::println;
con2.accept("bbb");
}
注意:Lambda 表达实体中调用方法的参数列表、返回类型必须和函数式接口中抽象方法保持一致
类::静态方法
@Test
public void test03(){
BiPredicate<String, String> bp1 = (x, y) -> x.equals(y);
System.out.println(bp1.test("a","b"));
BiPredicate<String, String> bp2 = String::equals;
System.out.println(bp2.test("c","c"));
}
类::实例方法
@Test
public void test03(){
BiPredicate<String, String> bp1 = (x, y) -> x.equals(y);
System.out.println(bp1.test("a","b"));
BiPredicate<String, String> bp2 = String::equals;
System.out.println(bp2.test("c","c"));
}
注:第一个参数是方法的调用者,第二个参数是方法的参数
2.构造器引用
格式:ClassName::new
@Test
public void test(){
Supplier<List> sup1 = () -> new ArrayList();
Supplier<List> sup2 = ArrayList::new;
}
这种构造器引用是看你参数的,比如有的是无参构造器,有的是一个参数构造器,有的是两个参数构造器,甚至更多,还有的是这几个全都有
比如:
public class student {
private int id;
private String name;
public student() {
}
public student(int id) {
this.id = id;
}
public student(int id, String name) {
this.id = id;
this.name = name;
}
}
这种就是全都有,这时候你传入几个参数他就按照几个参数的构造器来执行,如下:
@Test
//这种是无参构造器
public void a()
{
Supplier<student> q=()->new student();
Supplier<student> a=student::new;
student student = a.get();
System.out.println(student);
}
//这种是一个参数构造器 @Test
public void b()
{
// 这个Integer就是一个参数,就是id
Function<Integer,student> f=(x)->new student(x);
Function<Integer,student> f1=student::new;
//这个就是他传入的参数,就是id为1
student apply = f1.apply(1);
}
//这个是两个参数,几个参数就是用几个参数的函数式接口,无参可以用Supplier,一参Function,两参BiFunction
@Test
public void c()
{
BiFunction<Integer,String,student> b=(x,y)->new student(x,y);
BiFunction<Integer,String,student> b1=student::new;
student lisi = b1.apply(2, "lisi");
}
注意:需要调用的构造器的参数列表要与函数时接口中抽象方法的参数列表保持一致
3.数组引用
Type :: new
@Test
public void c()
{
Function<Integer,String[]> f=(x)->new String[x];
String[] apply = f.apply(10);
Function<Integer,String[]> f1=String[]::new;
String[] apply1 = f1.apply(1);
}
以上是关于java8新特性的主要内容,如果未能解决你的问题,请参考以下文章