新特性简介
- 速度更快(HashMap的实现优化)
- 代码更少(Lambda表达式)
- Stream API
- 最大化减少空指针异常(Optional)
- 其它API优化(接口和时间)
HashMap的实现优化
加载因子:大于等于加载因子事,需要将当前map数组长度扩大两倍,默认0.75(不可能是百分百,因为有些位置经过hashcode求余后可能一直都没有值)
共有代码
- 索引
索引 = 哈希 % (lengh -1)
- 哈希
1.7和1.8实现原理不同,但原则是一样的:让hash的高位同样参与 & 运算,保证散列的均匀性
- 求余
% 比 & 运算慢很多,因此尽量使用 & 运算代替.若 $b = 2^n$,则a % b = a & (b - 1 ).原因是$b = 2^n$时, 则 b-1 的二进制低位均为1,与 a 做 & 运算能截取到对应低位值即余数.
因此当 length 为 2 的整数次幂时,索引 = 哈希 & (lengh -1)
- 大小为 2 的整数次幂
- 求余运算可使用 & 运算代替,速度快
- 快速得到低位均为1的二进制数(length -1 ),保证了 & 运算的意义,否则低位出现0,& 运算后该位必是0,散列的均匀性被破坏
1.7的实现
哈希
1 | int h = hashSeed; |
添加
- 将要添加的元素hashcode求值并对map数组长度求余作为该元素要添加的位置/索引
- 如果有冲突且相等,则覆盖
- 如果有冲突且不相等,则在当前位置形成链表,后添加的元素放在第一个
查询
- 对要查询元素求得索引(通过上面添加操作描述的方法)
- 如果只有一个元素,直接返回
- 如果存在多个元素,遍历比较,如果某元素和查询元素相等则返回
删除
- 通过查询操作获取对应元素,然后将其删除
扩容
- 需要将当前数组长度扩大两倍,并需要对所有(数组+链表)元素重新求得索引并放入
1.8的实现
哈希
1 | hash = hash ^ (hash >>>16) |
添加
- 将要添加的元素hashcode求值并对map数组长度求余作为该元素要添加的位置/索引
- 如果有冲突且相等,则覆盖
- 如果有冲突且不相等且冲突小于等于8,则在当前位置形成链表,后添加的元素放在第一个
- 如果有冲突且不相等且冲突大于8,如果此时table长度小于等于64,则进行resize(),扩大table的长度
- 如果有冲突且不相等且冲突大于8,如果此时table长度大于64,则形成红黑树
查询
- 对要查询元素求得索引(通过上面添加操作描述的方法)
- 如果只有一个元素,直接返回
- 如果存在多个元素,根据该处结构查询元素并返回
删除
- 通过查询操作获取对应元素,然后将其删除
- 如果此树节点数量过小,将当前红黑树结构转换为链表结构返回并替换
- 否则删除此节点,并按照红黑树原理平衡,且把根节点放入map数组对应索引下
扩容
- 同样需要将当前数组长度扩大两倍,遍历map数组,如果当前节点为链表同1.7实现
- 如果当前节点为红黑树,则将红黑树按照索引规则(hash & oldLength == 0)分为两个链表,如果等于0说明位置不变,如果不等于0说明需要将该链表放入新的索引下(oldIndex + oldLength);同时根据链表长度,如果小于6则转变为正常链表结构放入当前索引/新索引下,如果大于6则转变为正常红黑树结果放入当前索引/新索引下.
Lambda表达式
四大函数接口
函数式接口: 只定义了一个抽象方法的接口(Object类的public方法除外),就是函数式接口,并且还提供了注解:@FunctionalInterface.
- Consumer
: 消费型接口,有参无返回值( void accept(T t)
) - Supplier
: 供给型接口,无参有返回值( T get()
) - Function<T,R>: 函数式接口,有参有返回值(
R apply(T t)
) - Predicate
: 断言型接口,有参有返回值( boolean test(T t)
)
方法引用
静态方法引用
1
2
3
4
5
6// * 被引用的方法参数列表和函数式接口中抽象方法的参数一致!!
// * 接口的抽象方法没有返回值,引用的方法可以有返回值也可以没有
// * 接口的抽象方法有返回值,引用的方法必须有相同类型的返回值!!
// 类名::静态方法
Function<Integer, Integer, Integer> biFun1 = (x, y) -> Integer.compare(x, y);
BiFunction<Integer, Integer, Integer> biFun2 = Integer::compare;对象方法引用
1
2
3
4
5
// * 同上
//对象::实例方法 对象也可使用 this 或 super
Consumer<Integer> con1 = (x) -> System.out.println(x);
Consumer<Integer> con2 = System.out::println;特定方法引用
1
2
3
4// * 同上
//类名:实例方法 第一个参数为实际调用者,第二个参数为方法形参(无参也可以,与实例方法形参一致即可)
BiFunction<String, String, Boolean> fun1 = (str1, str2) -> str1.equals(str2);
BiFunction<String, String, Boolean> fun2 = String::equals;构造方法引用
1
2
3
4
5
6
7
8
9
10
11// * 被引用的类必须存在一个构造方法与函数式接口的抽象方法参数列表一致
// 构造方法引用 类名::new
Supplier<Employee> sup = () -> new Employee();
System.out.println(sup.get());
Supplier<Employee> sup2 = Employee::new;
System.out.println(sup2.get());
// 构造方法引用 类名::new (带一个参数,多个参数也可以,只要满足与接口方法参数列表一致)
Function<Integer, Employee> fun = (x) -> new Employee(x);
Function<Integer, Employee> fun2 = Employee::new;
System.out.println(fun2.apply(100));数组引用
1
2
3
4// * 同上
// 数组引用
Function<Integer, String[]> fun = (x) -> new String[x];
Function<Integer, String[]> fun2 = String[]::new;
Stream API
Stream 操作有三个步骤: 创建->操作->终止操作
创建
1 | // 1.校验通过Collection 系列集合提供的stream()或者paralleStream() |
操作
1 | /** |
终止操作
1 | /** |
Optional容器
1 |
|
其它
接口增加默认实现或静态方法
1 | public interface Person { |
在JDK1.8中很多接口会新增方法,为了保证1.8向下兼容,1.7版本中的接口实现类不用每个都重新实现新添加的接口方法,引入了default默认实现,static的用法是直接用接口名去调方法即可。当一个类继承父类又实现接口时,若后两者方法名相同,则优先继承父类中的同名方法,即“类优先”,如果实现两个同名方法的接口,则要求实现类必须手动声明默认实现哪个接口中的方法。
新的日期API LocalDate | LocalTime | LocalDateTime
LocalDate
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24//获取当前日期,只含年月日 固定格式 yyyy-MM-dd 2018-05-04
LocalDate today = LocalDate.now();
// 根据年月日取日期,5月就是5,
LocalDate oldDate = LocalDate.of(2018, 5, 1);
// 根据字符串取:默认格式yyyy-MM-dd,02不能写成2
LocalDate yesteday = LocalDate.parse("2018-05-03");
// 如果不是闰年 传入29号也会报错
LocalDate.parse("2018-02-29");
//2018-05-04
LocalDate today = LocalDate.now();
// 取本月第1天: 2018-05-01
LocalDate firstDayOfThisMonth = today.with(TemporalAdjusters.firstDayOfMonth());
// 取本月第2天:2018-05-02
LocalDate secondDayOfThisMonth = today.withDayOfMonth(2);
// 取本月最后一天,再也不用计算是28,29,30还是31: 2018-05-31
LocalDate lastDayOfThisMonth = today.with(TemporalAdjusters.lastDayOfMonth());
// 取下一天:2018-06-01
LocalDate firstDayOf2015 = lastDayOfThisMonth.plusDays(1);
// 取2018年10月第一个周三 so easy?: 2018-10-03
LocalDate thirdMondayOf2018 = LocalDate.parse("2018-10-01").with(TemporalAdjusters.firstInMonth(DayOfWeek.WEDNESDAY));LocalTime
1
2
3
4
5//16:25:46.448(纳秒值)
LocalTime todayTimeWithMillisTime = LocalTime.now();
//16:28:48 不带纳秒值
LocalTime todayTimeWithNoMillisTime = LocalTime.now().withNano(0);
LocalTime time1 = LocalTime.parse("23:59:59");LocalDateTime
1
2
3
4
5
6
7
8//转化为时间戳 毫秒值
long time1 = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();
long time2 = System.currentTimeMillis();
//时间戳转化为localdatetime
DateTimeFormatter df= DateTimeFormatter.ofPattern("YYYY-MM-dd HH:mm:ss.SSS");
System.out.println(df.format(LocalDateTime.ofInstant(Instant.ofEpochMilli(time1),ZoneId.of("Asia/Shanghai"))));