1. HashMap 在数组-链表的基础上加上了红黑数(在碰撞的个数大于8时,并且总容量大于64时)将链表转为红黑树;ConcurrentHashMap采用了CAS算法。
2. 1.8后内存结构发生改变,原来的方法区——堆中的永久区(PremGen)将被从堆中分离,取而代之的为元空间(MetaSpace),与之前的不同的是它直接使用物理内存,PremGenSize和MaxPremGenSize这两个调优参数将无效,取而代之的是MetaSpaceSize和MaxMetaSpaceSize
3.1 Lamdba是一个匿名函数,Llamdba表达式可以被理解为一段可传递的代码(将代码想数据一样传递);Lambda表达式需要“函数式接口”(接口中只有一个抽象方法的接口)的支持,用@FunctionalInterface修饰,可检测是否为函数式接口;
/*Lambda表达式基础语法:"->"该操作符将Lambda表达式分为两部分
*左侧:Lambda表达式的参数列表
*右侧:Lambda表达式中所需执行的功能,即Lambda体
*
*语法格式一:无参数无返回值
* 接口 对象 =()-> Lambda体
*
*语法格式二:有一个参数无返回值
* 接口 对象 =(参数)-> Lambda体
*
*语法格式三:有一个参数小括号可以不写
* 接口 对象 = 参数 -> Lambda体
*
*语法格式四:有两条以上参数,有返回值,并且Lambda体中有多条语句
* 接口 对象 = (参数,参数)->{
* Lambda体
* return 返回值;
* };
*
*语法格式五:有两条以上参数,有返回值,并且Lambda体中只有一条语句
* 接口 对象 = (参数,参数)->Lambda体
*
*语法格式六:Lambda表达式的参数列表的数据类型可以不写,因为可以根据上下文进行类型推断
*/
3.2 java8提供的四大内置核心函数式接口与其子接口
/**
* 四大核心接口
* Consumer : 消费型接口
* void accept(T t);
* Supplier : 供给型接口
* T get();
* Function : 函数型接口
* R apply(T t);
* Predicate : 断言型接口
* boolean test(T t);
*/
public class test{
@Test
public void test1() {
happy(1000,(money)-> System.out.println("特殊消费:"+money+"元"));
}
public void happy(double money, Consumer con){
con.accept(money);
}
@Test
public void test2(){
List list = getNumList(10,()->(int)(Math.random() * 100));
for(Integer num : list){
System.out.println(num);
}
}
public List getNumList(int num, Supplier sup){
List list = new ArrayList<>();
for(int i =0;istr.trim());
System.out.println(s);
}
public String strHandler(String str, Function fun){
return fun.apply(str);
}
@Test
public void test4(){
List list = Arrays.asList("你好啊!","你是","看","不到","我的","你只能看到我!");
List s = filterStr(list,(st)->st.length() >3);
for(String str : s){
System.out.print(str);
}
}
public List filterStr(List list, Predicate pre){
List strList = new ArrayList<>();
for(String str : list){
if(pre.test(str)){
strList.add(str);
}
}
return strList;
}
}
/**其他接口
* BiFunction 参数类型 T U 返回类型 R
* UnaryOperator 参数类型 T 返回类型 T
* BinaryOperator 参数类型 T T 返回类型 T
* BiConsumer 参数类型 T U 返回类型 void
* ToIntFunction 参数类型 T 返回类型 int
* ToLongFunction 参数类型 T 返回类型 long
* ToDoubleFunction 参数类型 T 返回类型 double
* IntFunction 参数类型 int 返回类型 R
* LongFunction 参数类型 long 返回类型 R
* DoubleFunction 参数类型 double 返回类型 R
*/
3.3 若Lamdba体中的内容有方法已经实现了我们就可以使用“方法引用”,语法格式:对象::实例方法名,类::静态方法名,类::实例方法名(第一个参数是方法的调用者,第二个参数是方法的参数时可用);构造器引用格式:ClassName::new; 数组引用:Type[]::new; 以上用法都应遵行Lamdba体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保持一致。
4.1 流(Stream)是数据渠道,用与操作数据源(集合,数组等)所生成的元素序列(集合讲的是数据,流讲的是计算);Stream的操作步骤:创建Stream(一个数据源获取一个流)——>中间操作——>终止操作(产生一个新的流,数据源不变)
4.1.1创建Stream的方法
//1.可以通过Collection系列集合提供的stream()(此方法获取的是串型流)或parallelStream()(此方法获取的是并型流)
//示例
List list = new ArrayList<>();
Stream st1 = list.stream();
//2.通过Arrays中的静态方法stream()获取数据流
//示例
Employee[] emps = new Employee[10];
Stream st2 = Arrays.Stream(emps);
//3.通过Stream类中的静态方法of()
//示例
Stream st3 = Stream.of("aa","bb","cc");
//4.创建无限流
//示例
//迭代
Stream st4 = Stream.iterate(0,(x)->x+2);
//生成
Stream.generate(()->Math.random())
4.1.2 Stream的中间操作
/**
*筛选与切片操作
*filter--接收Lamdba,从流中通过条件筛选
*limit--截断流,使其元素不超过给定数量。
*skip(n)--跳过元素,返回一个扔掉了前n个元素的流,若该流中元素不足 n个,则返回一个空流,与limit(n)互补
*distinct--筛选,通过流所生成元素的hashCode()和equals()去除重复元素
*/
//将多个FlyPig对象装入List中,使用前提是有一个名为FlyPig的bean
List fp = Arrays.asList(
new FlyPig("aaaa","AAAA","1111"),
new FlyPig("bbbb","BBBB","2222"),
new FlyPig("cccc","CCCC","3333"),
new FlyPig("dddd","DDDD","4444"),
new FlyPig("eeee","EEEE","5555"),
new FlyPig("aaaa","AAAA","1111"),
new FlyPig("aaaa","AAAA","1111")
);
@Test
public void test1(){
fp.stream()
.filter((e)->e.getName().equals("aaaa"))//中间操作
.forEach(System.out::println);//终止操作
}
@Test
public void test2(){
fp.stream()
.limit(2)
.forEach(System.out::println);//终止操作
}
@Test
public void test3(){
fp.stream()
.skip(2)
.forEach(System.out::println);//终止操作
}
@Test
public void test4(){
fp.stream()
.distinct()//去重的要求是bean中有写hashCode()和equals()方法
.forEach(System.out::println);//终止操作
}
/**
* 映射
* map--接收Lamdba,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
* flatMap--接收一个函数作为参数,将流中的每一个值都换成另一个流,然后把所有流链接成一个流
*/
@Test
public void test5(){
List list = Arrays.asList("aaa","bbb","ccc","ddd");
list.stream()
.map((str)->str.toUpperCase())
.forEach(System.out::println);
System.out.println("-------------------------------------------");
fp.stream()
.map(FlyPig::getName)
.map((str)->str.toUpperCase())
.forEach(System.out::println);
System.out.println("-------------------------------------------");
// Stream> stream= list.stream()
// .map(streamTest::filterCharacter);//{{a,a,a},{b,b,b},{c,c,c},{d,d,d}}
// stream.forEach((sm)->{
// sm.forEach(System.out::println);
// });
System.out.println("-------------------------------------------");
list.stream()
.flatMap(streamTest::filterCharacter)//{a,a,a,b,b,b,c,c,c,d,d,d}
.forEach(System.out::println);
}
public static Stream filterCharacter(String str){
List list = new ArrayList<>();
for(Character ch : str.toCharArray()){
list.add(ch);
}
return list.stream();
}
/**
* 排序
* sorted()--自然排序
* sorted(Comparator com)--定制排序
*/
@Test
public void test7(){
List list = Arrays.asList("cccc","aa","aaaa","bb","b");
list.stream()
.sorted()
.forEach(System.out::println);
System.out.println("-------------------------");
fp.stream()
.sorted((e1,e2)->{
if(e1.getCar().equals(e2.getCar())){
return e1.getName().compareTo(e2.getName());
}else{
return -e1.getCar().compareTo(e2.getCar());
}
}).forEach(System.out::println);
}
4.1.3 Stream的终止操作
/**
* @author ZSP
* @Project: javaTest
* @Package:test
* @date 2018/9/5 10:59
* @Description: Stream的终止操作
**/
public class streamTest2 {
List employees = Arrays.asList(
new Employee("张三",14,3426.99, Employee.Status.FREE),
new Employee("李四",15,3332.34, Employee.Status.BUSY),
new Employee("王五",34,4322.23, Employee.Status.VOCATION),
new Employee("赵六",23,2433.56, Employee.Status.FREE),
new Employee("田七",25,2442.78, Employee.Status.BUSY)
);
/**
* 查找与匹配
* allMatch--检查是否匹配所有元素
* anyMatch--检查是否至少匹配一个元素
* noneMatc--检查是否是没有匹配所有元素
* findFirst--返回第一个元素
* findAny--返回当前流中的任意元素
* count--返回流中元素的总个数
* max--返回流中的最大值
* min--返回流中的最小值
*/
@Test
public void test1(){
boolean b1=employees.stream()
.allMatch((e)->e.getStatus().equals(Employee.Status.BUSY));
System.out.println(b1);
System.out.println("---------------------------------");
boolean b2=employees.stream()
.anyMatch((e)->e.getStatus().equals(Employee.Status.BUSY));
System.out.println(b2);
System.out.println("---------------------------------");
boolean b3=employees.stream()
.noneMatch((e)->e.getStatus().equals(Employee.Status.BUSY));
System.out.println(b3);
System.out.println("---------------------------------");
Optional op1 =employees.stream()
.sorted((e1,e2)->Double.compare(e1.getSlary(),e2.getSlary()))
.findFirst();
System.out.println(op1.get());
System.out.println("---------------------------------");
Optional op2 =employees.parallelStream()
.filter((e)->e.getStatus().equals(Employee.Status.FREE))
.findAny();
System.out.println(op2.get());
}
@Test
public void test2(){
long count = employees.stream()
.count();
System.out.println(count);
Optional op1 = employees.stream()
.max((e1,e2)->Double.compare(e1.getSlary(),e2.getSlary()));
System.out.println(op1);
Optional op2 = employees.stream()
.map(Employee::getSlary)
.min(Double::compare);
System.out.println(op2);
}
/**
* 归约
* reduce(T identity,BinaryOperator)/reduce(BinaryOperator)--可以将流中元素反复结合起来,得到一个值。
*/
@Test
public void test3(){
List list = Arrays.asList(1,2,3,4,5,6,7,8,9);
Integer sum = list.stream()
.reduce(0,(x,y)->x+y);
System.out.println(sum);
System.out.println("------------------------");
Optional op = employees.stream()
.map(Employee::getSlary)
.reduce(Double::sum);
System.out.println(op);
}
/**
* 收集
* collect--将流转换为其他形式,接受一个Collector接口的实现,用于给Stream中元素做汇总的方法
*/
@Test
public void test4(){
List list = employees.stream()
.map(Employee::getName)
.collect(Collectors.toList());
list.forEach(System.out::println);
System.out.println("-------------------");
Set set = employees.stream()
.map(Employee::getName)
.collect(Collectors.toSet());
set.forEach(System.out::println);
}
@Test
public void test(){
//总数
Long count = employees.stream()
.collect(Collectors.counting());
System.out.println(count);
System.out.println("-----------------------------");
//平均值
Double avg = employees.stream()
.collect(Collectors.averagingDouble(Employee::getSlary));
System.out.println(avg);
System.out.println("-----------------------------");
//总和
Double sum= employees.stream()
.collect(Collectors.summingDouble(Employee::getSlary));
System.out.println(sum);
System.out.println("-----------------------------");
//最大值
Optional op1 = employees.stream()
.collect(Collectors.maxBy((e1,e2)->Double.compare(e1.getSlary(),e2.getSlary())));
System.out.println(op1.get());
System.out.println("-----------------------------");
//最小值
Optional op2 = employees.stream()
.map(Employee::getSlary)
.collect(Collectors.minBy(Double::compare));
System.out.println(op2);
}
//分组
@Test
public void test6(){
Map> map = employees.stream()
.collect(Collectors.groupingBy(Employee::getStatus));
System.out.println(map);
for(Map.Entry> entry : map.entrySet()){
System.out.println(entry);
}
}
//多级分组
@Test
public void test7(){
Map>>map = employees.stream()
.collect(Collectors.groupingBy(Employee::getStatus,Collectors.groupingBy((e)->{
if((e).getAge()<=50){
return "青年";
}else if((e).getAge()<=50){
return "中年";
}else {
return "老年";
}
})));
System.out.println(map);
}
//分区
@Test
public void test8(){
Map> map =employees.stream()
.collect(Collectors.partitioningBy((e)->e.getSlary()>3000));
System.out.println(map);
}
@Test
public void test9(){
DoubleSummaryStatistics dss = employees.stream()
.collect(Collectors.summarizingDouble(Employee::getSlary));
System.out.println(dss.getSum());
System.out.println(dss.getAverage());
System.out.println(dss.getMax());
}
@Test
public void test10(){
String str = employees.stream()
.map(Employee::getName)
.collect(Collectors.joining(","));
System.out.println(str);
}
}
5. java8的并行流采用的ForkJoin框架(java7就有了只不过用起来很麻烦)——将任务不断的拆分为更小的任务,然后将小的任务加入到线程队列中进行运算,再将结果合起来的方式;该框架的核心特点为“工作窃取模式”——当一个线程中没有任务时会随机的去其他线程的队列末尾“偷”一个任务来进行;并行流的用法在java8中得到了简化,用起来十分方便——同过Stream API 中的parallel()与sequential()即可切换并行流与顺序流
/**
* 并行流与顺序流的用法,以及效果演示
*/
@Test
public void test1(){
//开始时间
Instant start = Instant.now();
// 用于计算0,到10000000000的累加和,parallel()让其使用并行流来进行运算,sequential()让其使用顺序流来进行运算
LongStream.rangeClosed(0,10000000000L)
.parallel()
.reduce(0,Long::sum);
//结束时间
Instant end = Instant.now();
System.out.println("耗时:"+Duration.between(start,end).toMillis()+"毫秒");
}
6. Optional
public class OptionalTest {
/*
Optional 容器类的常用方法
Optional.of(T t) : 创建一个Optional实例
Optional.empty() : 创建一个空的Optional实例
Optional.OfNullable(T t) : 若t不为null,创建Optional实例,否则创建空实例
isPresent() : 判断是否包含值
orElse(T t) : 如果调用对象包含值,返回该值,否则返回t
orElseGet(Supplier s) : 如果调用对象包含值,返回该值,否则返回s获得的值
map(Function f) : 如果有值对其处理,并返回处理后的Optional,否则返回Optional.empty()
flatMap(Function mapper) : 与map类似,要求返回值必须是Optional
*/
/**
* Optional.of(T t) : 创建一个Optional实例
* 如果传入的为空那么运行时创建实例这行会直接报错
*/
@Test
public void test1(){
Optional op = Optional.of(new Employee());
Employee emp = op.get();
System.out.println(emp);
}
/**
* Optional.empty() : 创建一个空的Optional实例
* 可以创建空的实例,但是空的实例是不能get()的
*/
@Test
public void test2(){
Optional op = Optional.empty();
System.out.println(op.get());
}
/**
* Optional.OfNullable(T t) : 若t不为null,创建Optional实例,否则创建空实例
* 可以创建空的实例,但同样不能get()
*
* isPresent() : 判断是否包含值
*
* orElse(T t) : 如果调用对象包含值,返回该值,否则返回t
*
* orElseGet(Supplier s) : 如果调用对象包含值,返回该值,否则返回s获得的值
*/
@Test
public void test3(){
Optional op = Optional.ofNullable(null);
//如果op有值就进入if
if(op.isPresent()){
System.out.println(op.get());
}
//如果op中有值则emp中装的是op中的值,如果op中没有值则emp中装的是下面新new的对象
Employee emp = op.orElse(new Employee("张三",18,4032.33,Employee.Status.FREE));
System.out.println(emp);
//如果op中有值则emp中装的是op中的值,如果op中没有值则emp中装的是op.orElseGet()括号中的功能
Employee emp2 = op.orElseGet(()->new Employee());
System.out.println(emp2);
}
/**
* map(Function f) : 如果有值对其处理,并返回处理后的Optional,否则返回Optional.empty()
*
* flatMap(Function mapper) : 与map类似,要求返回值必须是Optional
*/
@Test
public void test4(){
Optional op = Optional.ofNullable(new Employee("张三",18,4032.33,Employee.Status.FREE));
//将容器中的对象应用到了map中的函数上
Optional str = op.map((e)->e.getName());
System.out.println(str.get());
//要求必须将结果包装的Optional中
Optional str2 = op.flatMap((e)->Optional.of(e.getName()));
System.out.println(str2.get());
}
}
7. java8接口中允许有默认方法——用default修饰的接口它的实现方法可以直接写在接口中,当这个接口被调用时遵从“类优先”的原则;接口中还允许有静态方法。
8. java8出了一套全新的时间日期API,以解决线程安全问题。
8.1 新时间API的本地时间与时间戳
public class LocalDatetimeTest {
/**
* LocalData 日期
* LocalTieme 时间
* LocalDateTime 日期时间
*
* 这三个的用法一样
*/
@Test
public void test1(){
//获取当前时间
LocalDateTime ldt = LocalDateTime.now();
System.out.println(ldt);
//创建一个时间
LocalDateTime ldt2= LocalDateTime.of(2020,12,12,0,0,0);
System.out.println(ldt2);
//加一个小时
LocalDateTime ldt3 = ldt2.plusHours(1);
System.out.println(ldt3);
//减8分钟
LocalDateTime ldt4 = ldt3.minusMinutes(8);
System.out.println(ldt4);
//对时间拆分
System.out.println(ldt.getYear()+"年");
System.out.println(ldt.getMonthValue()+"月");
System.out.println(ldt.getDayOfMonth()+"日");
}
/**
* Instant : 时间戳(以 Unix 元年:1970年1月1日00:00:00 到某一个时间之间的毫秒值)
*/
@Test
public void test2(){
//默认的是 UTC 时区
Instant ins1 = Instant.now();
System.out.println(ins1);
//做偏移量运算
OffsetDateTime odt = ins1.atOffset(ZoneOffset.ofHours(8));
System.out.println(odt);
//显示毫秒
System.out.println(ins1.toEpochMilli());
//从Unix元年开始做运算
Instant ins2 = Instant.ofEpochSecond(1);
System.out.println(ins2);
}
/**
* Duration:计算两个“时间”之间的间隙
* Period:计算两个“日期”之间的间隙
*/
@Test
public void test3() throws InterruptedException {
Instant ins1 = Instant.now();
Thread.sleep(1000);
Instant ins2 = Instant.now();
Duration duration = Duration.between(ins1,ins2);
System.out.println(duration.toMillis());
System.out.println("---------------------------------");
LocalTime lt1 = LocalTime.now();
Thread.sleep(1000);
LocalTime lt2 = LocalTime.now();
System.out.println( Duration.between(lt1,lt2));
System.out.println("---------------------------------");
LocalDate ld1 = LocalDate.of(2011,2,5);
LocalDate ld2 = LocalDate.now();
Period period = Period.between(ld1,ld2);
System.out.println(period);
System.out.println(period.getYears());
System.out.println(period.getMonths());
System.out.println(period.getDays());
}
}
8.2 时间校正器
/**
* TemporalAdjuster:时间校正器
*/
@Test
public void test4(){
LocalDateTime ldt = LocalDateTime.now();
System.out.println(ldt);
//十月的这一天
LocalDateTime ldt2 = ldt.withDayOfMonth(10);
System.out.println(ldt2);
//下一个星期天
LocalDateTime ldt3 = ldt.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
System.out.println(ldt3);
}
8.3 时间的格式化
/**
* DataTimeFormatter:格式化时间/日期
*/
@Test
public void test6(){
//使用系统自带的格式
DateTimeFormatter dtf = DateTimeFormatter.ISO_DATE;
LocalDateTime ldt = LocalDateTime.now();
String strData = ldt.format(dtf);
System.out.println(strData);
System.out.println("---------------------");
//使用自定义的格式
DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
String strData2 = ldt.format(dtf2);
System.out.println(strData2);
//将格式解析回默认的
LocalDateTime newDate = ldt.parse(strData2,dtf2);
System.out.println(newDate);
}
8.4 时区的处理
/**
* ZonedDate、ZonedTime、ZoneDateTime
*/
@Test
public void test7(){
//查看支持的时区
Set set = ZoneId.getAvailableZoneIds();
set.forEach(System.out::println);
//选择的时区
LocalDateTime ldt = LocalDateTime.now(ZoneId.of("America/New_York"));
System.out.println(ldt);
LocalDateTime ldt2 = LocalDateTime.now(ZoneId.of("America/New_York"));
ZonedDateTime zdt = ldt2.atZone(ZoneId.of("America/New_York"));
System.out.println(zdt);
}
9. java8对注解的改进,重复注解(一个类上可以重复的定义两次注解)与类型注解(java8没有内置,需要配合一些其他的框架使用);重复注解要想使用需要用@Repeatable来修饰该注解,并指定容器类