1.Map的改造、方法区的新实现以及接口的新规定
map的新实现
这个其实大家面试经常会遇到,但是其实也比较简单,记住就ok了
(1)Map的底层由数组加链表的方式改为了数组加链表加红黑树的实现方式
(2)对key进行hash,然后再根据数组长度取余之后的链表的头插法改为了尾插法
(3)ConcurrentHashMap由原来的分段加Hashtable的实现方式,变为了跟Map一样的方式,只是数据的线程安全采用了cas锁的机制
链表改红黑树的条件是链表的长度超过8,原因是,链表长度过长时,查询效率会变低,而红黑树的本质则是一棵有序的二叉树,而二叉树的遍历分为三种方式,左中右,中左右,右中左,具体采用的哪种遍历方式没关注,但是不管具体采用哪种方式,遍历不需要经过所有节点,每经过一个节点,就减少了差不多一半的元素的被命中的可能性,查询的性能会变得很高,而相应的插入的性能就变低了。
方法区的新实现
另外就是方法区的实现由堆的一块固定的内存称为永久代,转变为了元空间,且采用的是物理机器的内存,这个转变的可能原因是因为,永久代本身发生垃圾回收的概率极为苛刻,另外永久代其实主要存储的是我们的代码编译之后加载进内存的汇编指令和常量池,这一块儿可能发生变动比较少以及垃圾回收能节省的空间几乎可以忽略,所以就移出来了吧。
接口的新规定
接口可以有具有实现体的方法了,但必须制定为默认方法,关键字为:default
注意事项:
- (1) 当子类继承的类和实现的接口的默认方法,有同名方法时,优先调用继承父类的具体实现
- (2)当子类实现多接口时,多接口存在同名默认方法时,子类必须实现默认方法,指定调用的具体默认方法
2.Lambda函数式编程以及新符号(::)的方法调用
lambda表达式其实解决的是个匿名函数的问题,另外还给我们总结了四种类型的接口,不用我们的代码中去定义那么多其实并没有实际实现的java接口文件,直接采用默认的这四种类型的接口以及他们的子类即可
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
/**
* Lambda语法总结
* 通用写法: 一句处理代码的情况下 () -> code;
* 多句话的情况下 () -> {} ;
* 函数shi接口有四种:
* 1.消费型接口:Consumer(T t) 无返回值
* 2.供给型接口:Supplier(T) 无参数,有返回值
* 3.函数型接口:Function(T,R) 有参数且需要返回值的接口
* 4.判断型接口:Predicat(T) 返回True/False
*/
public class LambdaTest {
public static void main(String[] args){
/* // 原写法
Comparator<Integer> com = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1,o2);
}
};
TreeSet<Integer> treeSet = new TreeSet<>(com);
// lambda写法
Comparator<Integer> comparator = (x ,y) -> Integer.compare(x,y);
TreeSet<Integer> treeSetComparator = new TreeSet<>(comparator);*/
Consumer<Integer> consumer = x -> System.out.println("consumer:"+x);
consumer.accept(1);
Supplier<Integer> supplier = () -> 1;
Integer i = supplier.get();
System.out.println("supplier:"+i);
Function<Integer,Integer> function = x -> x+1;
Integer fi = function.apply(1);
System.out.println("function:"+fi);
Predicate<Integer> predicate = x -> x > 0;
boolean pFlag = predicate.test(1);
System.out.println("predicate:"+pFlag);
}
}
方法的调用:采用新的操作符
::
,这其实是对函数式编程的一种支持,进一步降低lambda的代码量
import java.io.PrintStream;
import java.util.Collections;
import java.util.List;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* 方法的调用:采用新的操作符(::),这其实是对函数式编程的一种支持,进一步降低lambda的代码量
* ::用法又分为以下几种:
* 1. 实例对象 :: 实例方法名
* 2. 类名 :; 静态方法名
* 3. 类名 :: 实例方法名
* 构造函数其实也是:类名 :: new
* 数组的构造:类名[] :: new
* 注意点:
* 1.方法的参数以及返回值的确定是由你定义接收的函数式接口来进行推断得到的
* 2.调用重载的方法时,由你定义的函数式接口的参数类型来推断的,也就是你要调用的
* 方法的参数列表必须跟函数式接口定义的参数列表一直
* 3.类名 :: 实例方法这种调用方式其实是对于参数中有一个是实例方法的调用者时使用的,比如equlas方法
*/
public class MethodCalledTest {
public static void main(String[] args){
// 1.实例对象 :: 实例方法名
Consumer<String> consumer = (x) -> System.out.println(x);
PrintStream out = System.out;
Consumer<String> consumer1 = out::println;// 实际使用经常为System.out::println
// 2. 类名 :: 静态方法名
List<Integer> list = Collections.emptyList();
Supplier<List<Integer>> supplier = Collections::emptyList;
list = supplier.get();
// 2. 类名 :: 实例方法名
BiPredicate<String,String> biPredicate = (x,y) -> x.equals(y);
biPredicate = String::equals;
// 构造方法的使用 ClassName :: new
Supplier<String> supplier1 = String::new;
Function<String,String> function = String::new;// 调用String的有参构造函数
// 数组 类名[] :: new
Function<Integer,String[]> function1 = String[]::new;
String[] array = function1.apply(10);// Integer 为数组长度
}
}
3.Stream 流式操作
Stream Api 通过一个管道对集合和数组进行一系列的操作
-
stream() : 串行流,数据处理是一个个进行的,线程安全
-
parallelStream() :并行流,数据处理是多线程处理的,线程不安全,但是大数据量情况下,性能好,实现方式,其实就是Fork/Join框架的方式,充分利用多核的cpu
- Stream Api 包含三个步骤:
- 1.创建流
- 2.流式操作
- 3.终止操作
- 流有三个特点:
-
1.Stream本身不存储元素
-
2.Stream不会改变源对象,它的操作都会返回一个持有结果的新的Stream对象
-
3.Stream操作是延迟执行的,它只有等到终止操作时才会去真正的进行计算,称为“惰性求值”
创建流的方式:
/**
* 创建流有四种方式
* 1.通过集合本身提供的stream()或者parallelStream()方法创建
* 2.通过Arrays的静态方法Arrays.stream(T t)创建
* 3.通过Stream类本身的静态方法 Stream.of("aa","bb","cc")创建
* 4.无限流有两种创建方式
* 1)迭代创建流Stream.iterate(0,x -> x+2),需要一个参数和一个有参数有返回值的lambda表达式
* 2)生成创建流 Stream.generate(() -> Math.random()),需要一个供给型的lambda的表达式
*/
public void createStream(){
// 1.通过Collection集合的stream()或者parallelStream() 创建流
List<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream();
Stream<String> parallelStream = list.parallelStream();
// 2.通过Arrays的静态方法stream(),创建流
String[] strArray = new String[]{};
Stream<String> stream2 = Arrays.stream(strArray);
// 3.通过Stream类本身的静态方法of创建
Stream<String> stream3 = Stream.of("a","b","c");
// 4.无限流
Stream<Integer> stream4 = Stream.iterate(0,x -> x +2 );
Stream<Double> stream5 = Stream.generate(() -> Math.random());
}
流水线的操作:
筛选与切片
/**
* 1.筛选与切片
* (1)filter - 接収一个断言类型的lambda,过滤掉不满足的条件的元素
* (2)limit(n) - 截断流,收集不超过给定数量的元素,收集满了就不继续进行了
* (3)skip(n) - 跳过元素,返回一个丢掉了前n个元素的流,若元素不足n个返回空流,与limit互补
* (4)distinct - 筛选去掉重复元素,筛选通过的是元素的hashCode()和equals()方法
*/
public void opreateStream1(){
List<Integer> list = Arrays.asList(1,0,3,2,6,5,4);
// filter - 接収一个断言类型的lambda,过滤掉不满足的条件的元素
Stream stream = list.stream()
.filter(x -> {
// 可以注释掉stream.forEach,可以看到这个打印不执行
// 这个就是我们说的惰性求值,只有收集结果才执行中间操作
System.out.println(x);
return x > 0;
});
stream.forEach(System.out :: println);
// limit(n) - 截断流,收集不超过给定数量的元素,收集满了就不继续进行了
list.stream()
.filter(x -> {
// 这里除了上面的还可以看到断路器的作用,找到了满足条件的结果,后面的就不执行了
System.out.println(x);
return x > 0;
}).limit(1).forEach(System.out :: println);// 终止操作才能看到结果
// skip(n) - 跳过元素,返回一个丢掉了前n个元素的流,若元素不足n个返回空流,与limit互补
list.stream()
.filter(x -> x > 0)
.skip(2)
.forEach(System.out :: println);
// distinct - 筛选去掉重复元素,筛选通过的是元素的hashCode()和equals()方法
list.stream()
.filter(x -> x > 0)
.distinct()
.forEach(System.out :: println);
}
映射
/**
* 2.映射
* (1)map - 接収一个函数型接口的lambda,应用于每个元素,并返回一个新的元素,最终返回一个流
* (2)flatMap - 接收一个函数作为参数,将该函数应用于每个元素上并返回一个流,每个元素产生的流再组合在一起产生一个新流返回
*/
public void opreateStream2(){
List<String> list = Arrays.asList("1","0","3","2");
// map - 接収一个函数型接口的lambda,应用于每个元素,并返回一个新的元素
List<Integer> list1 = list.stream()
.map(str -> Integer.valueOf(str)) // 将String转换成为了Integer
.collect(Collectors.toList());
// flatMap - 接收一个函数作为参数,将该函数应用于每个元素上并返回一个流,每个元素产生的流再组合在一起产生一个新流返回
List<String> listList = Arrays.asList("abc","bcd");
List<Stream<Character>> characterListList = listList.stream()
.map(StreamTest :: getElementChar) // 结果为{{a,b,c},{b,c,d}}
.collect(Collectors.toList()); // 收集结果为一个流的集合
List<Character> characterList = listList.stream()
.flatMap(StreamTest :: getElementChar) // 结果为{a,b,c,b,c,d}
.collect(Collectors.toList()); // 收集结果为一个char的集合
}
public static Stream<Character> getElementChar(String str){
List<Character> characters = new ArrayList<>();
for (Character c: str.toCharArray()) {
characters.add(c);
}
return characters.stream();
}
排序
/**
* 3.排序
* (1)sorted - 自然排序,按照每个元素对象内部实现的CompareTo方法排序
* (2)sorted(Comparator m) - 定制排序,接收一个比较器的匿名函数,按照匿名函数的比较方法排序
*/
public void opreateStream3(){
List<Integer> list = Arrays.asList(1,0,3,2);
// sorted - 自然排序,按照每个元素对象内部实现的CompareTo方法排序
list.stream()
.sorted()
.forEach(System.out::println);
// sorted(Comparator m) - 定制排序,接收一个比较器的匿名函数,按照匿名函数的比较方法排序
list.stream()
.sorted((x ,y) -> {
if(x < y){
return y;
}else {
return x;
}
})
.forEach(System.out::println);
}
终止操作:
查找与匹配
/**
* 查找与匹配
* (1)allMatch - 接收一个断言lambda表达式,判断是否所有元素都满足这个断言
* (2)anyMatch - 接收一个断言lambda表达式,判断是否至少有一个元素满足这个断言
* (3)noneMatch - 接收一个断言lambda表达式,判断是否所有元素都不满足这个断言
* (4)findFirst - 查找第一个元素,返回一个Optional,防止空指针
* (5)findAny - 查找任意一个元素,返回一个Optional,防止空指针
* (6)count - 返回流中元素的个数
* (7)max - 返回流中元素的比较的最大的结果
* (8)min - 返回流中元素的比较的最小的结果
*/
public void finishStream(){
List<Integer> list = Arrays.asList(1,0,3,2);
// allMatch - 接收一个断言lambda表达式,判断是否所有元素都满足这个断言
boolean flag = list.stream()
.allMatch(x -> x > 2);
System.out.println(flag);
// anyMatch - 接收一个断言lambda表达式,判断是否至少有一个元素满足这个断言
boolean flag1 = list.stream()
.anyMatch(x -> x > 2);
System.out.println(flag1);
// noneMatch - 接收一个断言lambda表达式,判断是否所有元素都不满足这个断言
boolean flag2 = list.stream()
.noneMatch(x -> x > 2);
System.out.println(flag2);
// findFirst - 查找第一个元素,返回一个Optional,防止空指针
Optional<Integer> optional =list.stream()
.filter(x -> x >2)
.findFirst();
System.out.println(optional.get());
// findAny - 查找任意一个元素,返回一个Optional,防止空指针
Optional<Integer> optional1 =list.stream()
.filter(x -> x >2)
.findAny();
System.out.println(optional1.get());
// count - 返回流中元素的个数
long count =list.stream()
.filter(x -> x >2)
.count();
System.out.println(count);
// max - 返回流中元素的比较的最大的结果
Optional<Integer> optional2 = list.stream()
.max((x ,y) -> {
if(x < y){
return y;
}else {
return x;
}
});
System.out.println(optional2.get());
}
规约
map和reduce通常进行连接使用,称为map-reduce模式,大数据的处理,经常使用这种模式
/**
* 规约
* (1)reduce - reduce(T identity, BinaryOperator<T> accumulator) 第一个参数为起始指定值,且identity做为二元运算
* 的T参数进行一个表达式的运算的结果作为二元运算的T进行下一次计算,直到流中元素全部处理完
* (2)reduce - Optional<T> reduce(BinaryOperator<T> accumulator)无起始值,直接计算结果
*/
public void finishStream1(){
List<Integer> list = Arrays.asList(1,0,3,2);
/**
* reduce - reduce(T identity, BinaryOperator<T> accumulator) 第一个参数为起始指定值,且identity做为二元运算
* 的T参数进行一个表达式的运算的结果作为二元运算的T进行下一次计算,直到流中元素全部处理完
*
*/
Integer sum = list.stream()
.reduce(0,(x,y) -> x+y);
System.out.println(sum);
/**
* reduce - Optional<T> reduce(BinaryOperator<T> accumulator)
* 无起始值,直接计算结果
*/
Optional<Integer> optional = list.stream()
.reduce((x,y) -> x+y);
System.out.println(optional.get());
}
收集
/**
* 收集
* (1)Collector - 接收Collector接口的匿名函数,进行流的元素的收集
*/
public void finishStream2(){
List<Integer> list = Arrays.asList(1,0,3,2,1,2);
// Collector - Collectors.toList()
List<Integer> list1 = list.stream()
.filter(x -> x > 1)
.collect(Collectors.toList());
// Collector - Collectors.toSet()
Set<Integer> set = list.stream()
.filter(x -> x > 1)
.collect(Collectors.toSet());
// Collector - Collectors.toCollection()
HashSet<Integer> set1 = list.stream()
.filter(x -> x > 1)
.collect(Collectors.toCollection(HashSet::new));
// Collector - Collectors.counting() 元素总个数
long count = list.stream()
.filter(x -> x > 1)
.collect(Collectors.counting());
// Collector - Collectors.averagingDouble() 元素的平均值
Double aDouble = list.stream()
.filter(x -> x > 1)
.collect(Collectors.averagingDouble(x -> Double.valueOf(x)));
// Collector - Collectors.maxBy() 最大值
Optional<Integer> integer = list.stream()
.filter(x -> x > 1)
.collect(Collectors.maxBy(Integer::compare));
// Collector - Collectors.minBy() 最小值
Optional<Integer> integer1 = list.stream()
.filter(x -> x > 1)
.collect(Collectors.minBy(Integer::compare));
// Collector - Collectors.groupingBy() 分组
// Collector - Collectors.partitioningBy() 分区
// Collector - Collectors.summarizingDouble() 总结 可以获取平均值,最大最小值等
// Collector - Collectors.joining() 连接
}
4.Optipnal防止空指针的新的api类
- Optional容器类:为了解决空指针异常而存在的
- Optional容器类并不能避免空指针的问题,但是能快速定位到空指针的位置,并且将常见的一些空判断放入到容器中,如果判断层次够深,则将if,换成.后面是Optional的各种方法这样代码比较整齐
- 常用方法:
-
(1) Optional.of(T value) value不能为空,为空直接抛空指针异常
-
(2) Optional.empty() 创建一个空的容器,调用optional.get()抛空指针异常
-
(3)Optional.ofNullable(T value) 当value为空的时候,调用optional.get()抛空指针异常
-
(4)Optional.isPresent() 判断容器中是否有值
-
(5)optional.orElse(T value) 当容器中没有值的时候,创建一个value的默认值,返回的也是这个默认值,有则返回容器中的值
-
(6)optional.orElseGet(Suplier s) 容器中有值返回值,没值进行函数式接口处理
-
(7)optional.map(Function t) 当容器中有值进行函数的处理,返回处理后的值,没有值,返回一个空的容器Optional.empty()
-
(8)optional.flatMap(Function map) 与map类似,但是返回值一定是Optional
5.新的日期时间Api
新的时间Api
- 区别:
-
原时间Api,线程不安全,并且有背于我们常用的时间,故新的时间api全部放在了Java.time包下且采用了统一的国际标准
- 常用的类:
-
(1)LocalDateTime/LocalDate/LocalTime 三者的方法与实例化一致
-
(2)Instant:时间戳(以Unix元年<1970-01-01 00:00:00>开始到指定时间的毫秒值)
-
(3)duration 计算时间差值的类
-
(4)Period 计算日期差值的类
-
(5)TemporalAdjuster时间校正器 计算一个特殊的时间,TemporalAdjusters 提供很多常用的操作,比如下一个工作日
-
(6)DateTimeFormatter 时间/日期格式化器,DateTimeFormatter.ofPattern("yyyyMMdd")指定特殊格式
-
(7)ZoneDate/ZoneTime/ZoneDateTime 带时区的一些时间api