历史:
openjdk->sun公司开发的
Oraclejdk->oracle公司开发,基于openjdk
lambda表达式
public class a{
public static void main(String[] args){
//开启一个线程
new Thread(new Runnable(){
public void run(){
System.out.println("线程运行");
}
}).start();
System.out.println("主线程运行");
}
}
注解
lambda表达式原理
编译时生成一个匿名内部类。
接口
jdk1.8之前只有1.常量2.抽象方法
jdk1.8之后可以有1.常量2.抽象方法3.默认方法4.静态方法
Stream流
fliter:
public class StreamFilter {
public static void main(String[] args) {
List<String> list = Arrays.asList("张三", "张三丰", "李四", "王五");
//1.获取所有姓张的信息
//2.获取名称长度为3的用户
//3.输出所有的用户信息
ArrayList<Object> list1 = new ArrayList<>();
for (String s : list) {
if (s.startsWith("张") && s.length()==3){
list1.add(s);
}
}
for (Object o : list1) {
System.out.println(o);
}
//stream流
list.stream()
.filter(a->a.startsWith("张"))
.filter(a->a.length()==3)
.forEach(System.out::println);
}
}
stream流的获取方式
-
根据Collection获取
Collection接口增加了一个default接口
new ArrayList().stream();
new HashSet().stream();
...
map集合没有实现Collection接口,不能直接使用,可以转为set集合使用
-
通过Steam的of方法(主要操作数组)
Steam接口有一个静态方法of()
String[] arr1={"aa","bb","cc"};
Stream.of(arr1);
Stream a1=Stream.off("a1","a2","a3");
//注意:基本数据类型的数组是不可以的
forEach
forEach用来遍历集合
//stream流
List<String> list = Arrays.asList("张三", "张三丰", "李四", "王五");
list.stream().forEach(System.out::println);
count
统计元素的个数
List<String> list = Arrays.asList("张三", "张三丰", "李四", "王五");
list.stream().count();
fliter
过滤数据,返回符合条件的数据
List<String> list = Arrays.asList("张三", "张三丰", "李四", "王五");
//stream流
list.stream()
.filter(a->a.startsWith("张"))
.filter(a->a.length()==3)
.forEach(System.out::println);
limit
分页,取前n个数据
List<String> list = Arrays.asList("张三", "张三丰", "李四", "王五");
list.stream().limit(2).forEach(System.out::println);
skip
跳过前n个数据
List<String> list = Arrays.asList("张三", "张三丰", "李四", "王五");
list.stream().skip(2).forEach(System.out::println);
map
类型转换,一种类型转换为另一种,包括基本类型和对象类型
Stream.of("1","2","3","4","5","6")
.map(Integer::parseInt)
.forEach(System.out::println);
sorted
排序
//自然排序
Stream.of("1","2","3","4","5","6")
.map(Integer::parseInt)
.sorted()
.forEach(System.out::println);
System.out.println("降序");
Stream.of("1","2","3","4","5","6")
.map(Integer::parseInt)
.sorted((a,b)->{return b-a;})
.forEach(System.out::println);
distinct
去除重复
//去重
Stream.of("1","2","2","3","3","4","5","6")
.map(Integer::parseInt)
.distinct()
.forEach(System.out::println);
如果是对象的话根据对象的的hashcode和equals方法判断
flatMap 使用
flatMap 方法用于映射每个元素到对应的结果,一对多。
示例:从句子中得到单词
String worlds = "The way of the future";
List<String> list7 = new ArrayList<>();
list7.add(worlds);
List<String> list8 = list7.stream().flatMap(str -> Stream.of(str.split(" ")))
.filter(world -> world.length() > 0).collect(Collectors.toList());
System.out.println("单词:");
list8.forEach(System.out::println);
// 单词:
// The
// way
// of
// the
// future
Stream流的peek使用
peek对每个元素执行操作并返回一个新的Stream
示例: 双重操作
System.out.println("peek使用:");
Stream.of("one", "two", "three", "four").filter(e -> e.length() > 3).peek(e -> System.out.println("转换之前: " + e))
.map(String::toUpperCase).peek(e -> System.out.println("转换之后: " + e)).collect(Collectors.toList());
// 转换之前: three
// 转换之后: THREE
// 转换之前: four
// 转换之后: FOUR
Stream流的Match使用
-
allMatch:Stream 中全部元素符合则返回 true ;
-
anyMatch:Stream 中只要有一个元素符合则返回 true;
-
noneMatch:Stream 中没有一个元素符合则返回 true。
示例:数据是否符合
boolean all = lists.stream().allMatch(u -> u.getId() > 3);
System.out.println("是否都大于3:" + all);
boolean any = lists.stream().anyMatch(u -> u.getId() > 3);
System.out.println("是否有一个大于3:" + any);
boolean none = lists.stream().noneMatch(u -> u.getId() > 3);
System.out.println("是否没有一个大于3的:" + none);
// 是否都大于3:false
// 是否有一个大于3:true
// 是否没有一个大于3的:false
Stream流的iterate使用
iterate 跟 reduce 操作很像,接受一个种子值,和一个UnaryOperator(例如 f)。然后种子值成为 Stream 的第一个元素,f(seed) 为第二个,f(f(seed)) 第三个,以此类推。在 iterate 时候管道必须有 limit 这样的操作来限制 Stream 大小。
示例:生成一个等差队列
System.out.println("从2开始生成一个等差队列:");
Stream.iterate(2, n -> n + 2).limit(5).forEach(x -> System.out.print(x + " "));
// 从2开始生成一个等差队列:
// 2 4 6 8 10
reduce
Integer reduce = Stream.of("1", "2", "2", "3", "3", "4", "5", "6")
.map(Integer::parseInt)
.reduce(0, (a, b) -> a + b);//求和 0+1+2+2+3+3+4+5+6
System.out.println(reduce);
map+reduce
Integer a=Stream.of(
new Person("zhangsan",18)
,new Person("zhangsan",18)
,new Person("zhangsan",18)
,new Person("zhangsan",18))
.map(p->p.getAge())
.reduce(0,(a,b)->a+b);
System.out.println(a);
mapToInt
Integer arr[]={1,2,3,5,6,8};
IntStream int=Stream.off(arr)
.mapToInt(Integer::intValue);
concat
两个流合并成一个流可以使用
Stream<String> a=Stream.of("a","b","c");
Stream<String> b=Stream.of("x","y","z");
Stream.concat(a,b).forEach(System.out::println);
Stream结果收集
1.到集合
List<Integer> list = Stream.of("1", "2", "2", "3", "3", "4", "5", "6")
.map(Integer::parseInt)
.collect(Collectors.toList());
Set<Integer> set = Stream.of("1", "2", "2", "3", "3", "4", "5", "6")
.map(Integer::parseInt)
.collect(Collectors.toSet());
ArrayList<Integer> arrayList = Stream.of("1", "2", "2", "3", "3", "4", "5", "6")
.map(Integer::parseInt)
.collect(Collectors.toCollection(ArrayList::new));
HashSet<Integer> hasSet = Stream.of("1", "2", "2", "3", "3", "4", "5", "6")
.map(Integer::parseInt)
.collect(Collectors.toCollection(HashSet::new));
2.到数组
Object[] objects = Stream.of("1", "2", "2", "3", "3", "4", "5", "6")
.toArray();
Integer[] array = Stream.of("1", "2", "2", "3", "3", "4", "5", "6")
.toArray(Integer[]::new);
3.对流中的数据进行聚合计算
sum max min avg count
max
Optional<Integer> collect = Stream.of("1", "2", "2", "3", "3", "4", "5", "6")
.map(Integer::valueOf)
.collect(Collectors.maxBy((a, b) -> a - b));
min
Optional<Integer> collect = Stream.of("1", "2", "2", "3", "3", "4", "5", "6")
.map(Integer::valueOf)
.collect(Collectors.maxBy((a, b) -> b-a));
sum
Integer collect = Stream.of("1", "2", "2", "3", "3", "4", "5", "6")
.map(Integer::valueOf)
.collect(Collectors.summingInt(a->a));
System.out.println(collect);
avg
Double collect = Stream.of("1", "2", "2", "3", "3", "4", "5", "6")
.map(Integer::valueOf)
.collect(Collectors.averagingInt(a->a));
System.out.println(collect);
count
Long collect = Stream.of("1", "2", "2", "3", "3", "4", "5", "6")
.map(Integer::valueOf)
.collect(Collectors.counting());
System.out.println(collect);
4.对流中的数据做分组操作
4.1一个字段
List<PeoplePO> list = new ArrayList<>();
list.add(new PeoplePO("张三", 93, 50.0));
list.add(new PeoplePO("李四", 53, 60.84));
list.add(new PeoplePO("王五", 30, 30.0));
list.add(new PeoplePO("赵六", 64, 230.5));
list.add(new PeoplePO("周七", 18, 88.0));
list.add(new PeoplePO("老八", 60, 23.0));
list.add(new PeoplePO("狗九", 29, 17.34));
Map<String, List<PeoplePO>> collect = list.stream().collect(Collectors.groupingBy(PeoplePO::getName));
collect.forEach((k,v)->System.out.println(k+":"+v));
Map<String, List<PeoplePO>> map = list.stream().collect(Collectors.groupingBy(a -> a.getAge() >= 18 ? "成年" : "未成年"));
map.forEach((k,v)->System.out.println(k+":"+v));
4.2多级分组
List<PeoplePO> list = new ArrayList<>();
list.add(new PeoplePO("张三", 3, 50.0));
list.add(new PeoplePO("李四", 53, 60.84));
list.add(new PeoplePO("王五", 30, 30.0));
list.add(new PeoplePO("赵六", 5, 230.5));
list.add(new PeoplePO("周七", 18, 88.0));
list.add(new PeoplePO("老八", 17, 23.0));
list.add(new PeoplePO("狗九", 29, 17.34));
Map<String, Map<String, List<PeoplePO>>> collect = list.stream()
.collect(Collectors.groupingBy(PeoplePO::getName, Collectors.groupingBy(p -> p.getAge() > 18 ? "成年" : "未成年")));
collect.forEach((k,v)-> System.out.println(k+":"+v));
5.分区
List<PeoplePO> list = new ArrayList<>();
list.add(new PeoplePO("张三", 3, 50.0));
list.add(new PeoplePO("李四", 53, 60.84));
list.add(new PeoplePO("王五", 30, 30.0));
list.add(new PeoplePO("赵六", 5, 230.5));
list.add(new PeoplePO("周七", 18, 88.0));
list.add(new PeoplePO("老八", 17, 23.0));
list.add(new PeoplePO("狗九", 29, 17.34));
list.stream().collect(Collectors.partitioningBy(p->p.getAge()>18)).forEach((k,v)->System.out.println(k+":"+v));
6.拼接
List<PeoplePO> list = new ArrayList<>();
list.add(new PeoplePO("张三", 3, 50.0));
list.add(new PeoplePO("李四", 53, 60.84));
list.add(new PeoplePO("王五", 30, 30.0));
list.add(new PeoplePO("赵六", 5, 230.5));
list.add(new PeoplePO("周七", 18, 88.0));
list.add(new PeoplePO("老八", 17, 23.0));
list.add(new PeoplePO("狗九", 29, 17.34));
String collect = list.stream().map(PeoplePO::getName).collect(Collectors.joining());
System.out.println(collect);
String collect2 = list.stream().map(PeoplePO::getName).collect(Collectors.joining("_"));
System.out.println(collect2);
String collect3 = list.stream().map(PeoplePO::getName).collect(Collectors.joining("_","pre","suf"));
System.out.println(collect3);
Stream并行和串行流
前面使用的都是串行流
1.串行流
List<PeoplePO> list = new ArrayList<>();
list.add(new PeoplePO("张三", 3, 50.0));
list.add(new PeoplePO("李四", 53, 60.84));
list.add(new PeoplePO("王五", 30, 30.0));
list.add(new PeoplePO("赵六", 5, 230.5));
list.add(new PeoplePO("周七", 18, 88.0));
list.add(new PeoplePO("老八", 17, 23.0));
list.add(new PeoplePO("狗九", 29, 17.34));
list.stream().filter(a->{
System.out.println(Thread.currentThread());
return a.getAge()>18;}).count();
//都在一个线程执行
2.并行流
parallelStream并行流
2.1获取
List<PeoplePO> list = new ArrayList<>();
list.add(new PeoplePO("张三", 3, 50.0));
list.add(new PeoplePO("李四", 53, 60.84));
list.add(new PeoplePO("王五", 30, 30.0));
list.add(new PeoplePO("赵六", 5, 230.5));
list.add(new PeoplePO("周七", 18, 88.0));
list.add(new PeoplePO("老八", 17, 23.0));
list.add(new PeoplePO("狗九", 29, 17.34));
//1.直接获取
Stream<PeoplePO> parallelStream = list.parallelStream();
//2.转换获取
Stream<PeoplePO> parallel = list.stream().parallel();
2.2操作
List<PeoplePO> list = new ArrayList<>();
list.add(new PeoplePO("张三", 3, 50.0));
list.add(new PeoplePO("李四", 53, 60.84));
list.add(new PeoplePO("王五", 30, 30.0));
list.add(new PeoplePO("赵六", 5, 230.5));
list.add(new PeoplePO("周七", 18, 88.0));
list.add(new PeoplePO("老八", 17, 23.0));
list.add(new PeoplePO("狗九", 29, 17.34));
list.parallelStream().filter(
a->{
System.out.println(Thread.currentThread());
return a.getAge()>18;
}
).count();
fork/join并行流底层算法
Optional类(处理空指针)
1.以前
String a =null;
if (a != null) {
System.out.println(a.length());
} else {
System.out.println("sda");
}
2.Optional类
//of方法
Optional<String> optional = Optional.of("zhangsan");
//Optional<Object> o = Optional.of(null); 不支持传入空
//ofNullable方法
Optional<String> zhangsan = Optional.ofNullable("zhangsan");
Optional<Object> o = Optional.ofNullable(null); //支持null
//empty
Optional<Object> empty = Optional.empty();
获取值
//of方法
Optional<String> optional = Optional.of("zhangsan");
//Optional<Object> o = Optional.of(null); 不支持传入空
System.out.println(optional.get());
//ofNullable方法
Optional<String> zhangsan = Optional.ofNullable("zhangsan");
Optional<Object> o = Optional.ofNullable(null); //支持null
System.out.println(o.get());//NoSuchElementException: No value present
//empty
Optional<Object> empty = Optional.empty();
提高:
isPresent():是否包含值
.orElse():如果包含值返回值,否则返回指定值
//of方法
Optional<String> optional = Optional.of("zhangsan");
//Optional<Object> o = Optional.of(null); 不支持传入空
if(optional.isPresent()){
System.out.println(optional.get());
}
//ofNullable方法
Optional<String> zhangsan = Optional.ofNullable("zhangsan");
Optional<Object> o = Optional.ofNullable(null); //支持null
if(o.isPresent()){
System.out.println(o.get());
}
System.out.println(o.orElse("aa"));
//empty
Optional<Object> empty = Optional.empty();
LocalDateTime
格式化
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter isoLocalDateTime = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
System.out.println(now.format(isoLocalDateTime));
System.out.println(now.format(dateTimeFormatter));
LocalDateTime parse = LocalDateTime.parse("2020-01-01 12:00:00", dateTimeFormatter);
System.out.println(parse);
介绍
JDK1.8除了新增了lambda表达式、stream流之外,它还新增了全新的日期时间API。在JDK1.8之前,Java处理日期、日历和时间的方式一直为社区所诟病,将 java.util.Date设定为可变类型,以及SimpleDateFormat的非线程安全使其应用非常受限。因此推出了java.time包,该包下的所有类都是不可变类型而且线程安全。
关键类
-
Instant:瞬时时间。
-
LocalDate:本地日期,不包含具体时间, 格式 yyyy-MM-dd。
-
LocalTime:本地时间,不包含日期. 格式 yyyy-MM-dd HH:mm:ss.SSS 。
-
LocalDateTime:组合了日期和时间,但不包含时差和时区信息。
-
ZonedDateTime:最完整的日期时间,包含时区和相对UTC或格林威治的时差。
使用
1.获取当前的日期时间
通过静态工厂方法now()来获取当前时间。
//本地日期,不包括时分秒
LocalDate nowDate = LocalDate.now();
//本地日期,包括时分秒
LocalDateTime nowDateTime = LocalDateTime.now();
System.out.println("当前时间:"+nowDate);
System.out.println("当前时间:"+nowDateTime);
// 当前时间:2018-12-19
// 当前时间:2018-12-19T15:24:35.822
2.获取当前的年月日时分秒
获取时间之后,直接get获取年月日时分秒。
//获取当前的时间,包括毫秒
LocalDateTime ldt = LocalDateTime.now();
System.out.println("当前年:"+ldt.getYear()); //2018
System.out.println("当前年份天数:"+ldt.getDayOfYear());//172
System.out.println("当前月:"+ldt.getMonthValue());
System.out.println("当前时:"+ldt.getHour());
System.out.println("当前分:"+ldt.getMinute());
System.out.println("当前时间:"+ldt.toString());
// 当前年:2018
// 当前年份天数:353
// 当前月:12
// 当前时:15
// 当前分:24
// 当前时间:2018-12-19T15:24:35.833
3.格式化时间
格式时间格式需要用到DateTimeFormatter类。
LocalDateTime ldt = LocalDateTime.now();
System.out.println("格式化时间: "+ ldt.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")));
//格式化时间:2018-12-19 15:37:47.119
4.时间增减
在指定的时间进行增加/减少年月日时分秒。
LocalDateTime ldt = LocalDateTime.now();
System.out.println("后5天时间:"+ldt.plusDays(5));
System.out.println("前5天时间并格式化:"+ldt.minusDays(5).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); //2018-06-16
System.out.println("前一个月的时间:"+ldt2.minusMonths(1).format(DateTimeFormatter.ofPattern("yyyyMM"))); //2018-06-16
System.out.println("后一个月的时间:"+ldt2.plusMonths(1)); //2018-06-16
System.out.println("指定2099年的当前时间:"+ldt.withYear(2099)); //2099-06-21T15:07:39.506
// 后5天时间:2018-12-24T15:50:37.508
// 前5天时间并格式化:2018-12-14
// 前一个月的时间:201712
// 后一个月的时间:2018-02-04T09:19:29.499
// 指定2099年的当前时间:2099-12-19T15:50:37.508
5.创建指定时间
通过指定年月日来创建。
LocalDate ld3=LocalDate.of(2017, Month.NOVEMBER, 17);
LocalDate ld4=LocalDate.of(2018, 02, 11);
6.时间相差比较
比较相差的年月日时分秒。
示例一: 具体相差的年月日
LocalDate ld=LocalDate.parse("2017-11-17");
LocalDate ld2=LocalDate.parse("2018-01-05");
Period p=Period.between(ld, ld2);
System.out.println("相差年: "+p.getYears()+" 相差月 :"+p.getMonths() +" 相差天:"+p.getDays());
// 相差年: 0 相差月 :1 相差天:19
注:这里的月份是不满足一年,天数是不满足一个月的。这里实际相差的是1月19天,也就是49天。
示例二:相差总数的时间
ChronoUnit 日期周期单位的标准集合。
LocalDate startDate = LocalDate.of(2017, 11, 17);
LocalDate endDate = LocalDate.of(2018, 01, 05);
System.out.println("相差月份:"+ChronoUnit.MONTHS.between(startDate, endDate));
System.out.println("两月之间的相差的天数 : " + ChronoUnit.DAYS.between(startDate, endDate));
// 相差月份:1
// 两天之间的差在天数 : 49
注:ChronoUnit也可以计算相差时分秒。
示例三:精度时间相差
Duration 这个类以秒和纳秒为单位建模时间的数量或数量。
Instant inst1 = Instant.now();
System.out.println("当前时间戳 : " + inst1);
Instant inst2 = inst1.plus(Duration.ofSeconds(10));
System.out.println("增加之后的时间 : " + inst2);
System.out.println("相差毫秒 : " + Duration.between(inst1, inst2).toMillis());
System.out.println("相毫秒 : " + Duration.between(inst1, inst2).getSeconds());
// 当前时间戳 : 2018-12-19T08:14:21.675Z
// 增加之后的时间 : 2018-12-19T08:14:31.675Z
// 相差毫秒 : 10000
// 相毫秒 : 10
示例四:时间大小比较
LocalDateTime ldt4 = LocalDateTime.now();
LocalDateTime ldt5 = ldt4.plusMinutes(10);
System.out.println("当前时间是否大于:"+ldt4.isAfter(ldt5));
System.out.println("当前时间是否小于"+ldt4.isBefore(ldt5));
// false
// true
7.时区时间计算
得到其他时区的时间。
示例一:通过Clock时钟类获取计算
Clock时钟类用于获取当时的时间戳,或当前时区下的日期时间信息。
Clock clock = Clock.systemUTC();
System.out.println("当前时间戳 : " + clock.millis());
Clock clock2 = Clock.system(ZoneId.of("Asia/Shanghai"));
System.out.println("亚洲上海此时的时间戳:"+clock2.millis());
Clock clock3 = Clock.system(ZoneId.of("America/New_York"));
System.out.println("美国纽约此时的时间戳:"+clock3.millis());
// 当前时间戳 : 1545209277657
// 亚洲上海此时的时间戳:1545209277657
// 美国纽约此时的时间戳:1545209277658
示例二: 通过ZonedDateTime类和ZoneId
ZoneId zoneId= ZoneId.of("America/New_York");
ZonedDateTime dateTime=ZonedDateTime.now(zoneId);
System.out.println("美国纽约此时的时间 : " + dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")));
System.out.println("美国纽约此时的时间 和时区: " + dateTime);
// 美国纽约此时的时间 : 2018-12-19 03:52:22.494
// 美国纽约此时的时间 和时区: 2018-12-19T03:52:22.494-05:00[America/New_York]
Java 8日期时间API总结:
-
提供了javax.time.ZoneId 获取时区。
-
提供了LocalDate和LocalTime类。
-
Java 8 的所有日期和时间API都是不可变类并且线程安全,而现有的Date和Calendar API中的java.util.Date和SimpleDateFormat是非线程安全的。
-
主包是 java.time,包含了表示日期、时间、时间间隔的一些类。里面有两个子包java.time.format用于格式化, java.time.temporal用于更底层的操作。
-
时区代表了地球上某个区域内普遍使用的标准时间。每个时区都有一个代号,格式通常由区域/城市构成(Asia/Tokyo),在加上与格林威治或 UTC的时差。例如:东京的时差是+09:00。
-
OffsetDateTime类实际上组合了LocalDateTime类和ZoneOffset类。用来表示包含和格林威治或UTC时差的完整日期(年、月、日)和时间(时、分、秒、纳秒)信息。
-
DateTimeFormatter 类用来格式化和解析时间。与SimpleDateFormat不同,这个类不可变并且线程安全,需要时可以给静态常量赋值。DateTimeFormatter类提供了大量的内置格式化工具,同时也允许你自定义。在转换方面也提供了parse()将字符串解析成日期,如果解析出错会抛出DateTimeParseException。DateTimeFormatter类同时还有format()用来格式化日期,如果出错会抛出DateTimeException异常。
-
再补充一点,日期格式“MMM d yyyy”和“MMM dd yyyy”有一些微妙的不同,第一个格式可以解析“Jan 2 2014”和“Jan 14 2014”,而第二个在解析“Jan 2 2014”就会抛异常,因为第二个格式里要求日必须是两位的。如果想修正,你必须在日期只有个位数时在前面补零,就是说“Jan 2 2014”应该写成 “Jan 02 2014”。
-