//1.4 ArraysSS
**//1.5 String**
为什么需要新的API?
在Java8之前,由于JDK本身设计的问题,导致了存在的时间日期API使用起来不是很好用(Date类:赋值的语义不直观、toString的返回值包含JVM的默认时区CET;Calendar类:月份依旧从0计算、与Date共存难以选择;最大的问题是:本身可变难以维护、格式化时间DateFormat不是线程安全的),其语义也不是很好,比如Date表示的是时间以及日期,为了使得时间日期相关的API操作起来比较方便,语义更好,Java8中加入了新的时间以及日期操作,位于java.time包中
在java.time包中有许多的类,不过我们关心的主要有以下几个:
- LocalDate,日期操作类
- LocalTime,时间操作类
- LocalDateTime,日期时间
- Duration,时间间隔
- Period,日期间隔
1. LocalDate和LocalTime类
他们都是不可变的;LocalDate包含年、月、日、星期的相关属性;LocalTime包含时分秒的相关属性;
使用方法包括:取当前时间日期、赋值、取某个属性、判断属性、通过字符串转换来赋值、合并为日期+时间LocalDateTime类;
@Test
public void func_01() {
//正如其名,LocalDate表示的是本地的日期,根据OS的设置来获取对应的时区等信息,其用法也非常简单
//初始化赋值-获取某个时间的对象
LocalDate date = LocalDate.of(2016, 9, 11);
System.out.println(date);
//通过LocalDate对象可以获取年月日等信息,其API比较见名知义,就不展开介绍了
int year = date.getYear();
System.out.println(year);
Month month = date.getMonth();//返回月份 SEPTEMBER
System.out.println(month);
int day = date.getDayOfMonth();
System.out.println(day);
DayOfWeek dow = date.getDayOfWeek();
System.out.println(dow);//返回星期三 WEDNESDAY
int len = date.lengthOfMonth();//这个月9月 一共30天
System.out.println(len);
boolean leap = date.isLeapYear();//是否闰年 2016 是闰年
System.out.println(leap);
//获取当前日期
LocalDate today = LocalDate.now();
System.out.println(today);
//解析日期字符串,通过字符串赋值
LocalDate date1 = LocalDate.parse("2019-09-04");//这里单数的数字必须前面+0,如2019.9.4->"2019-09-04",否则DateTimeParseException;
System.out.println(date1);
}
@Test
public void func_02() {//LocalTime表示的是当前的时间,其主要用法如下
//获取当前时间
LocalTime time = LocalTime.now();
System.out.println(time);
System.out.println(time.getHour());
System.out.println(time.getMinute());
System.out.println(time.getSecond());
System.out.println(time.getNano());//精确到 纳秒
//解析时间字符串,通过字符串赋值
LocalTime time1 = LocalTime.parse("13:45:20");这里单数的数字必须前面+0,如2019.9.4->"2019-09-04",否则DateTimeParseException;
}
@Test
public void func_03() {//LocalDateTime表示的是日期以及时间LocalDate+LocalTime,大致等于以前的Date对象
LocalDateTime dt1 = LocalDateTime.of(LocalDate.now(), LocalTime.now());//通过两个对象LocalDate+LocalTime合并为LocalDateTime;
System.out.println(dt1);
LocalDateTime dt2 = LocalDateTime.of(2018, Month.JULY, 24, 9, 32, 20);
//几种赋值修改的方式,指定LocalDate或LocalTime
LocalDateTime dt3 = LocalDate.now().atTime(13, 45, 20);
LocalDateTime dt4 = LocalDate.now().atTime(LocalTime.now());
LocalDateTime dt5 = LocalTime.now().atDate(LocalDate.now());
//拆分成LocalDate和LocalTime
LocalDate date = dt1.toLocalDate();
LocalTime time = dt2.toLocalTime();
}
2. Instant类
@Test
public void func_04() { //Instant的设计初衷是为了机器使用,即仅包含秒-纳秒级的单位
System.out.println(Instant.ofEpochSecond(2));//从1970-1-1 00:00:00 加上2秒 即1970-1-1 00:02:00
System.out.println(Instant.ofEpochSecond(2, 2000000000L));//从1970-1-1 00:00:00 加上2秒和2亿纳秒 即1970-1-1 00:04:00
System.out.println(Instant.now());//也可以打印当前时间戳
}
3. Duration和Period类
@Test
public void func_05() throws InterruptedException {
//Duration表示的是两个"时间"的间隔,注意 不能 用于表示两个"日期"间隔;可以比较LocalTime与Instant,也可以定义一个时间间隔
LocalTime lt0 = LocalTime.now();
Thread.sleep(2000);
Duration d1 = Duration.between(LocalTime.now(), lt0);//传入两个可以比较LocalTime
System.out.println(d1);
Instant t0 = Instant.now();
Thread.sleep(1000);
Duration d3 = Duration.between(Instant.now(), t0);//传入两个Instant
System.out.println(Duration.between(t0, Instant.now()));//后者减去前者
System.out.println(d3);
//定义时间间隔
Duration threeMinutes = Duration.ofMinutes(3);
System.out.println(threeMinutes);
Duration twoMinutess = Duration.of(2, ChronoUnit.MINUTES);
System.out.println(twoMinutess);
}
@Test
public void func_06() {
//与Duration对应,Period表示的是两个日期的间隔,注意不能用来表示两个时间间隔
LocalDate ld0 = LocalDate.of(2018, 8, 5);//2018,8,3 2018,8,4
Period p1 = Period.between(ld0, LocalDate.now());
System.out.println(p1);
//定义日期间隔
Period tenDays = Period.ofDays(10);
System.out.println(tenDays);
Period threeWeeks = Period.ofWeeks(3);
System.out.println(threeWeeks);
}
4.修改时间的属性
@Test
public void func_07() {
//对于LocalDate/LocalTime,相对于get取出属性,也可以使用with修改属性,注意修改后的为一个新对象,不会改变原有的LocalDate/LocalTime对象
//with直观操作,注意这里每一步都有个date = ...的赋值操作;
LocalDate date = LocalDate.now();
System.out.println(date);
date = date.withDayOfMonth(10);//日期修改为:这个月的第10天
System.out.println(date);
date = date.withDayOfYear(5);//日期修改为:这一年的第5天
System.out.println(date);
date = date.withYear(2018);//日期修改为:年份2018
System.out.println(date);
date = date.withMonth(8);//日期修改为:月份8
System.out.println(date);
date = date.with(ChronoField.MONTH_OF_YEAR, 10);//日期修改为:月份10
System.out.println(date);
//语义式的修改操作
date = date.plusDays(11);//日期修改为:加11天
System.out.println(date);
date = date.minusWeeks(1);//日期修改为:减1周
System.out.println(date);
date = date.plus(6, ChronoUnit.MONTHS);//日期修改为:月份加6
System.out.println(date);
//测试会不会修改以前的值
date.plus(3, ChronoUnit.YEARS);//日期修改为:年份加6-但是没有赋值,因此结果没变
System.out.println(date);
}
@Test
public void func_01() {
//正如其名,LocalDate表示的是本地的日期,根据OS的设置来获取对应的时区等信息,其用法也非常简单
//初始化赋值-获取某个时间的对象
LocalDate date = LocalDate.of(2016, 9, 11);
System.out.println(date);
//通过LocalDate对象可以获取年月日等信息,其API比较见名知义,就不展开介绍了
int year = date.getYear();
System.out.println(year);
Month month = date.getMonth();//返回月份 SEPTEMBER
System.out.println(month);
int day = date.getDayOfMonth();
System.out.println(day);
DayOfWeek dow = date.getDayOfWeek();
System.out.println(dow);//返回星期三 WEDNESDAY
int len = date.lengthOfMonth();//这个月9月 一共30天
System.out.println(len);
boolean leap = date.isLeapYear();//是否闰年 2016 是闰年
System.out.println(leap);
//获取当前日期
LocalDate today = LocalDate.now();
System.out.println(today);
//解析日期字符串,通过字符串赋值
LocalDate date1 = LocalDate.parse("2019-09-04");//这里单数的数字必须前面+0,如2019.9.4->"2019-09-04",否则DateTimeParseException;
System.out.println(date1);
}
@Test
public void func_02() {//LocalTime表示的是当前的时间,其主要用法如下
//获取当前时间
LocalTime time = LocalTime.now();
System.out.println(time);
System.out.println(time.getHour());
System.out.println(time.getMinute());
System.out.println(time.getSecond());
System.out.println(time.getNano());//精确到 纳秒
//解析时间字符串,通过字符串赋值
LocalTime time1 = LocalTime.parse("13:45:20");这里单数的数字必须前面+0,如2019.9.4->"2019-09-04",否则DateTimeParseException;
}
@Test
public void func_03() {//LocalDateTime表示的是日期以及时间LocalDate+LocalTime,大致等于以前的Date对象
LocalDateTime dt1 = LocalDateTime.of(LocalDate.now(), LocalTime.now());//通过两个对象LocalDate+LocalTime合并为LocalDateTime;
System.out.println(dt1);
LocalDateTime dt2 = LocalDateTime.of(2018, Month.JULY, 24, 9, 32, 20);
//几种赋值修改的方式,指定LocalDate或LocalTime
LocalDateTime dt3 = LocalDate.now().atTime(13, 45, 20);
LocalDateTime dt4 = LocalDate.now().atTime(LocalTime.now());
LocalDateTime dt5 = LocalTime.now().atDate(LocalDate.now());
//拆分成LocalDate和LocalTime
LocalDate date = dt1.toLocalDate();
LocalTime time = dt2.toLocalTime();
}
5.复杂操作(带有语义的,"如本周的最后一天"...)
@Test
public void func_08() {
//with传入对象(他是一个方法工厂),语义式的修改日期/时间,更加灵活和直观
LocalDate date = LocalDate.now();
System.out.println(date);
date = date.with(TemporalAdjusters.next(DayOfWeek.MONDAY));//得到下一个最近的"周一"的日期
System.out.println(date);
date = date.with(TemporalAdjusters.lastDayOfMonth());//得到这个月最后一天的日期
System.out.println(date);
}
6.打印输出/解析时间
@Test
public void func_09() {
//date.format格式化时间的输出形式,注意,他是线程安全的
LocalDate date = LocalDate.now();
System.out.println(date);
System.out.println(date.format(DateTimeFormatter.BASIC_ISO_DATE));
System.out.println(date.format(DateTimeFormatter.ISO_LOCAL_DATE));//default格式
System.out.println(date.format(DateTimeFormatter.ofPattern("dd/MM/yyyy")));//yyyy表示显示4位的年份,若是yy则显示后两位数
System.out.println(date.format(DateTimeFormatter.ofPattern("dd MMMM yyyy", Locale.ENGLISH)));//带国际化的日期
}
7.处理时区
@Test
public void func_10() {
//时区相关:时区时间类ZoneDateTime类,时区名ZoneId类
ZoneId zomeZone = ZoneId.of("Europe/Rome");//这个"Europe/Rome"需要查,不匹配则会抛异常
ZoneId zomeZone1 = ZoneId.of("America/New_York");
System.out.println(TimeZone.getDefault().toZoneId());//默认在 亚洲/上海 Asia/Shanghai
LocalDateTime ldt = LocalDateTime.now();
System.out.println(ldt);
ZonedDateTime zdt = ldt.atZone(zomeZone);
System.out.println(zdt);
System.out.println(ldt.atZone(zomeZone1));
}
总结:本小节主要简单地介绍了Java8中新的日期以及时间操作API,由于新的API语义性非常好,所以也没有过多地展开,在需要的时候查一下文档就足够了;
( Java8中时间日期库的20个常用使用示例 Java8中时间日期库的20个常用使用示例_Java我人生的博客-CSDN博客_java8中日期 )
最后补充一下JDK8 API中新增的一些方法
JDK8 API的更新
- Map接口本身没有可用的stream()方法但是你可以在键key-值value上创建专门的流或者通过map.keySet().stream(),map.values().stream()和map.entrySet().stream();
Map类型不支持streams,不过Map提供了一些新的有用的方法来处理一些日常任务(对key-value的操作),如下:
//1.1 Map
//1.1 Map
@Test
public void func_01() {
Map<Integer, String> map = new HashMap<>();
// map.stream();//Map没有.stream方法,但是更新了一些方便的API
map.put(0, "init val-0");
for (int i = 0; i < 10; i++) {
map.putIfAbsent(i, "val-" + i);//putIfAbsent()//1.初始化Map,给key-value赋值,当key不存在/或者值为null时,才插入value
}
System.out.println(map);
//
map.forEach((id, val) -> System.out.println(id + " ^" + val));//2.forEach遍历 forEach(): val0 val1 val2 val3 val4 val5 val6 val7 val8 val9
//
map.computeIfPresent(3, (num, val) -> val + num);//3.第二个参数为BiFunction,通过key和value来计算value,并且把新value的计算结果put进去,当key不存在/或者值为null时才计算
System.out.println(map.get(3)); // “val-3”+3 = "val-33"
map.computeIfPresent(9, (num, val) -> null);
System.out.println(map.containsKey(9)); // 上一步key=9对应的value被置为null,返回false
map.computeIfPresent(11, (num, val) -> num + val);
System.out.println(map.containsKey(11)); // 不存在该key,返回false
//
map.computeIfAbsent(23, num -> "val" + num);//4.与上面类似,当key/value存在才计算;
System.out.println(map.containsKey(23)); // true
map.computeIfAbsent(3, num -> "33333");//IfAbsent为false,因此value不变
System.out.println(map.get(3)); // val-33
//接下来展示如何在Map里删除一个键值K V 全都匹配的项
System.out.println(map.remove(3, "val-3"));//5.删除完全匹配的k/v,没匹配成功,返回false
System.out.println(map.remove(3, "val-33"));//匹配成功,删除,返回true
System.out.println(map.get(3));//null
System.out.println(map.getOrDefault(33, "not found"));//6.取value值,返回原值或者默认值(如归原value为空),这一步只取值没有put操作,不修改map!
System.out.println(map.get(33));//null,上一步getOrDefault()的操作没有对map进行put操作,仅返回值
//对Map的元素做合并也变得很容易了:7.Merge和前面的compute类似,不传入key但是多了个初始值,做的事情是如果键名不存在则插入,否则则对原键对应的值和初始值做合并操作并重新插入到map中;
System.out.println(map.get(9)); //之前的状态:<9,null>
map.merge(9, "val-9", (value, newValue) -> value.concat(newValue));//不存在键名key,插入
System.out.println(map.get(9)); // val9
map.merge(9, "-concat", (value, newValue) -> value.concat(newValue));//存在key,执行后面的操作,把结果作为新value给put进去;
System.out.println(map.get(9));
}
//1.2 列表 List
//1.2 列表 List
@Test
public void func_02() {
List<Integer> list = Arrays.asList(1, 3, 5, 7, 9);
list.replaceAll((i) -> i + 1);//1.replaceAll,传入一个Function,相当于对Array做了流里面的map操作
System.out.println(list);
// list.removeIf(i -> i > 5);//注意这里会报错UnsupportedOperationException,因为list没有重写remove方法,需要转成ArrayList
List<String> al = new ArrayList<>();
al.add("1");
al.add("3");
al.add("5");
al.add("7");
al.add("9");
al.removeIf(i -> Integer.parseInt(i) > 5);//2.removeIf,传入一个Predicate,删除满足要求的list元素,需要注意该list必须重写了remove方法;
System.out.println(al);
}
//1.3 比较器 Comparator 配合流的sorted/集合类的sort
//1.3 比较器 Comparator 配合流的sorted/集合类的sort
@Test
public void func_03() {
//1.定义自己的Comparator,comparing方法传入一个Function,即对i处理后,在做compareTo比较
System.out.println(Stream.of(1, 3, 5, 7, 9, 2, 4, 6, 8, 10).sorted(Comparator.comparing(i -> Math.abs(5 - i))).collect(Collectors.toList()));//按照与5的距离排序;
//2.将比较器-逆序,comparing传入第二个参数reverseOrder/或者后面直接.reversed,注意:不能在传入lambda的Comparator后面直接加.reverse,会编译报错,需要显示的加上lambda的参数类型;
System.out.println(Stream.of(1, 3, 5, 7, 9, 2, 4, 6, 8, 10)
.sorted(Comparator.comparing(i -> Math.abs(5 - i), Comparator.reverseOrder()))
.collect(Collectors.toList()));//将比较器-逆序;
System.out.println(Stream.of(1, 3, 5, 7, 9, 2, 4, 6, 8, 10)
.sorted(Comparator.comparing((Integer i) -> Math.abs(5 - i)).reversed())
.collect(Collectors.toList()));//将比较器-逆序;
//3.如果比较结果相等,则继续比较,否则不触发thenComparing
System.out.println(Stream.of(1, 3, 5, 7, 9, 2, 4, 6, 8, 10)
.sorted(Comparator.comparing((Integer i) -> Math.abs(5 - i)).thenComparing(Comparator.reverseOrder()))
.collect(Collectors.toList()));//将比较器-逆序;
}
//1.4 Arrays
//1.4 Arrays
@Test
public void func_04() {
//1.setAll
int[] array0 = new int[1000000];
// Arrays.fill(array0,1);//只能指定值填充;
// System.out.println(Arrays.toString(array0));
Random r = new Random();
Arrays.setAll(array0, i -> i);//会打印出0-数组长度的自然数
Arrays.setAll(array0, i -> i * r.nextInt(5));//可以使用lambda
//System.out.println(Arrays.toString(array0));
//2.parallelSort
long start = System.nanoTime();
Arrays.sort(array0.clone());
System.out.println((System.nanoTime() - start) / 1000000 + " ms");
start = System.nanoTime();
Arrays.parallelSort(array0.clone());//parallelSort比sort速度提升很多
System.out.println((System.nanoTime() - start) / 1000000 + " ms");
//3.并行累积(前n项)累积运算
int[] array1 = new int[]{1, 3, 5, 7, 9, 2, 4, 6, 8, 10};
Arrays.parallelPrefix(array1, (a, b) -> a * b);//每一项为从当前索引的前n项累积两两运算的结果;
System.out.println(Arrays.toString(array1));
}
**//1.5 String**
//1.5 String
@Test
public void func_05() {//将列表或数组的每个String元素以指定的符号连接成一个String
//对List
List<String> list = new ArrayList<>();
list.add("Mxy");
list.add("String");
list.add("join");
String join = String.join("-", list);//传入String类型的List集合,使用"-"号拼接
System.out.println(join);
//对Array
String[] s = new String[]{"Hao", "Ming"};//传入String类型的数组,使用"-"号拼接
String join2 = String.join("-", s);
System.out.println(join2);
//Stream
String res = list.stream().collect(Collectors.joining(", "));
System.out.println(res);
}