一、JDK8新特性
1、hashMap优化
1.1、Jdk1.7中:hashmap(数组(16)-链表)
使用一个Entry数组存储数据,用key的hashcode取模来决定key会被分配到数组的位置,如果hashcode相同,或者hashcode取模后的结果相同(hash collison),那么这些key会被定位到Entry数组的同一个格子里,这些key会形成一个链表。如果在同一个格子里,会调用equals()方法,比较对象是否是同一个,若是同一个,则将值替换成最新的,若不是,将刚插入的对象放在最前面,其他依次后移,最终形成一个链表。
在hashcode特别差的情况下,比如说所有的key的hashcode都相同,这个链表可能很长,那么put/get操作都可能需要遍历这个链表。HashMap分配的空间有限,需要一个加载因子如果你hashmap的空间有100,那么当你插入了75个元素的时候,hashmap就需要扩容了,不然的话会形成很长散列桶,对于查询和插入都会增加时间,因为他要一个一个的equals。但是你又不能让加载因子很小,0.01这样是不合适的,因为他会大大消耗你的 内存, 你一加入一个对象hashmap就扩容。这时就存在着一个平衡,jdk中默认是0.75,可以根据自己的实际情况进行调整。
1.2、Jdk1.8 中:hashMap(数组-链表-红黑树)
使用一个Node数组来存储数据,但这个Node可能是链表结构,也可能是红黑树结构,如果插入的key的hashcode相同,那么这些key也会被定位到Node数组的同一个格子里。如果同一个格子的key不超过8个,使用链表结构存储。如果超过了8个,那么会调用treeifyBin()函数,将链表转换为红黑树。除添加之外其他效率都高。
注意:但是真正想要利用JDK1.8的好处,有一个限制:key的对象,必须正确的实现了Compare接口如果没有实现Compare接口,或者实现得不正确(比方说所有Compare方法都返回0)那JDK1.8的HashMap其实还是慢于JDK1.7的。
1.3、ConcurrentHashMap
在JDK1.7版本中,ConcurrentHashMap的数据结构是由一个Segment数组和多个HashEntry组成,如下图所示:
Segment数组的意义就是将一个大的table分割成多个小的table来进行加锁,也就是上面的提到的锁分离技术,而每一个Segment元素存储的是HashEntry数组+链表,这个和HashMap的数据存储结构一样。
JDK1.8的实现已经摒弃了Segment的概念,而是直接用Node数组+链表+红黑树的数据结构来实现,并发控制使用Synchronized和CAS来操作,整个看起来就像是优化过且线程安全的HashMap,虽然在JDK1.8中还能看到Segment的数据结构,但是已经简化了属性,只是为了兼容旧版本。
2、Lambda表达式
2.1、 Lambda表达式:
一个lambda可以由用逗号分隔的参数列表、–>符号与函数体三部分表示.引入特殊的操作符”->”
左侧:lambda 表达式参数列表。
右侧:lambda 表达式体,即要执行的功能。
语法一:无参数,无返回值,匿名内部类变量被final修饰
语法二:有一个参数,无返回值
Consumer consumer = (x)-> System.out.println(x);
consumer.accept("我真帅");
语法三:有两个以上参数,有返回值,lambda有多条语句。(如果只有一条语句,大括号和return都可以不写)
Comparator<Integer> com = (x,y)->{
System.out.println(".....");
return Integer.compare(x,y);
};
语法四:Lambda表达式的参数列表的数据类型可以不写,因为JVM的编译器可以通过上下文推断出。
Lambda表达式需要”函数式接口”的支持
函数是接口:接口中只有一个抽象方法的接口,称为函数式接口,可以使用注解@FunctionalInterface修饰,可以检查是否是函数式接口。
2.2、 Java8 内置的四大核心函数式接口
(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);
2.3 、方法的引用:
若Lambda体中的内容方法已经实现了,我们可以使用”方法引用”---Lambda的另一种表达形式。
主要有三种语法格式:
(1) 对象::实现方法名
Employee emp = new Employee();
Supplier<String> supplier = ()->emp.getName();
String name = supplier.get();
------>>>
Supplier<String> sup = Emp::getName;
String name = sup.get();
(2) 类::静态方法名
Comparator<Integer> com = (x,y)->Integer.compare(x,y);
Comparator<Integer> com1 = Integer::compare;
(3) 类::实例方法名
BiPredicate<String,String> bp = (x,y)-> x.equals(y); // x:实例方法的调用者
BiPredicate<String,String> bp2 = String::equals;
注意:①Lambda 体中调用方法的参数列表与返回值类型,要与函数式接口中的抽象方法的函数列表和返回值类型保持一致。
②若 Lambda参数列表中的第一参数是实例方法的调用者,而第二个参数是实例方法的参数时,可以使用CalssName::method。eg:类::实例方法名的实例
2.4、 构造器引用
2.4.1、 格式:ClassName::new
@Test
public void test7() {
Supplier<Employee> sup = () -> new Employee();
Employee employee = sup.get();
/*构造器引用/,无参构造*/
Supplier<Employee> supm = Employee::new;
System.out.println(supm.get()); ---get()无参
//有参构造
EmployeeInterFace<Integer,String,Integer,Double,Employee> supplier =(a,s,d,f)->new Employee(a,s,d,f);
EmployeeInterFace<Integer,String,Integer,Double,Employee> supplier1=Employee::new;
System.out.println(supplier1.grenter(102,"张三",23,4444.44));
}
//有参构造函数式接口
@FunctionalInterface
public interface EmployeeInterFace<T,R,E,F,G> {
public G grenter(T t,R r,E e,F f);
}
注意:构造器参数列表要与接口参数列表保持一致。
2.4.2、Stream:是数据渠道,用于操作数据源(集合、数组等)所产生的元素序列。”集合讲的是数据,流讲的是计算”
注意:① Stream自己不会存储元素。
② Stream不会改变源对象。相反,他们会返回一个持有结果的新的Stream。
③ Stream操作是延迟执行的。这意味着他们会等到需要结构的时候才去执行。
2.4.3、创建Stream的方式:
/*1.可以通过Collection系列集合提供的Stream()或者parallelStream()创建Stre