时间日期类-概述
生活中的时间
每个国家的时间,没有一个统一的标准,都是自己算自己的,行不行?
世界标准时间:格林尼治时间/格林威治时间(Greenwich Mean Time)简称 GMT
。现在提供的时间已经有些误差了,所以不再使用这个时间了,现在使用的是原子钟提供的时间
中国的标准时间:世界标准时间 +8小时 ,中国在东八区
时间换算公式
1秒 = 1000毫秒
1毫秒 = 1000微秒
1微秒 = 1000纳秒
计算机中的时间原点
计算机中的起始时间:1970年1月1日 00:00:00
原因:1969年8月,贝尔实验室的程序员肯汤普逊利用妻儿离开一个月的机会,开始着手创造一个全新的革命性的操作系统,他使用 B
编译语言在老旧的 PDP-7
机器上开发出了 Uix
的一个版本。随后,汤普逊和同事丹尼斯里奇改进了 B
语言,开发出了 C
语言,重写了 UNX
。
1970年1月1日算C语言的生日
1
2
3
// Long 是 long 的 包装类
long time = System . currentTimeMillis (); //打印的是从1970年1月1日到现在时间过了这么多毫秒
System . out . println ( time ); // 1575465416955
(图1
)
Calendar
日期计算,例如,从今天开始退后 65
天是第几天,今天是第几天
Timezone
是进行时区计算
时间日期类-Date构造方法
Date类概述和构造方法
Date
代表了一个特定时间,精确到毫秒
2020年1月1日 0:0:0
2030年1月1日 11:11:11
方法名
说明
public Date()
创建一个Date对象,表示默认时间
public Date(long date)
创建一个Date对象,表示指定时间
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class TestDemo {
public static void main ( String [] args ) {
//那么这个时间就表示电脑中的当前时间。
Date date1 = new Date ();
System . out . println ( date1 );
//从计算机的时间原点开始,过了指定毫秒的那个时间。
Date date2 = new Date ( 0L ); //L表示的是long长整型
System . out . println ( date2 ); //Thu Jan 01 08:00:00 CST 1970
//从时间原点开始,过了0毫秒。
//因为我们是在中国,我们是在东八区需要+8小时。
//1970年1月1日 上午的9点
//60分 * 60秒 = 3600秒,3600秒 * 1000 = 3600000 毫秒 (参数是毫秒,1秒=1000毫秒)
Date date3 = new Date ( 3600L * 1000 );
System . out . println ( date3 );
}
}
总结
Date date1 = new Date();
→ 把当前时间封装成一个 Date
对象
Date date2 = new Date(0L);
→ 把从时间原点开始,过了指定 毫秒
的时间,封装成一个 Date
对象,需要考虑时差问题。
时间日期类-Date成员方法
Date类的常用方法
方法名
说明
public long getTime()
获取Date所代表的毫秒值
public void setTime(long time)
修改Date中的毫秒值
getYear (过时)
获取的是"从1900年开始到现在过了多少年"
getMonth (过时)
获取当前的月份,获取的月是从0开始的(0-11)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class TestDemo {
public static void main ( String [] args ) {
//把当前时间封装成一个date对象
Date date1 = new Date ();
//获取这个date对象的毫秒值---获取当前时间的毫秒值
long time = date1 . getTime ();
System . out . println ( time ); //2461471445407
//getTime获得的毫秒值与currentTimeMillis获取的毫秒值进行对比,查看获取的毫秒值是否正确
long time2 = System . currentTimeMillis ();
System . out . println ( time2 ); //2461471445407
//将当前系统的时间包装成一个data2对象,再通过setTime()来将data2设置成距1970.1.1.0:0:0过去0秒的时间
Date date2 = new Date ();
date2 . setTime ( OL ); //跟new Date(0L)效果一样
System . out . println ( date2 ); //Thu Jan 01 08:00:00 CST 1970
}
}
SimpleDataFormat概述
1
2
3
4
5
6
// 代码
Date date1 = new Date ();
System . out . println ( date1 );
// 控制台输出
Wed Jan 01 15 : 00 : 22 CST 2048
我们发现上面打印的时间不符合我们的阅读习惯,我们之前看到的时间是 2020-11-27 08:11:39
SimpleDataFormat
可以对 Date
对象,进行 格式化和解析
格式化:Date对象
→ 2020年1月1日 0:0:0
解析:2020年1月1日 0:0:0
→ Date对象
常用的模式字母及对应关系如下:
2020-11-11 13:27:06
→ yyyy-MM-dd HH:mm:ss
2020年11月11日 13:27:06
→ yyyy年MM月dd日 HH:mm:ss
SimpleDataFormat的构造方法
方法名
说明
public SimpleDateFormat()
构造一个SimpleDateFormat,使用默认格式
public SimpleDateFormat(String pattern)
构造一个SimpleDateFormat,使用指定的格式
1
2
3
4
5
6
7
8
9
public class TestDemo {
public static void main ( String [] args ) {
//当前时间的Date对象
Date date = new Date ();
//创建了一个日期格式。
SimpleDateFormat sdf = new SimpleDateFormat ( "yyyy年MM月dd日 HH:mm:ss" );
}
}
SimpleDataFormat格式化和解析日期
格式化(从 Date
到 String
)
public final String format(Date date) :将日期格式化成日期/时间字符串
解析(从 String
到 Date
)
public Date parse(String source) :从给定字符串的开始,解析文本以生成日期
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class TestDemo {
public static void main ( String [] args ) {
//当前时间的Date对象
Date date = new Date ();
//创建了一个日期格式。
SimpleDateFormat sdf = new SimpleDateFormat ( "yyyy年MM月dd日 HH:mm:ss" );
String result1 = sdf . format ( date );
System . out . println ( result1 );
}
}
public class TestDemo {
public static void main ( String [] args ) {
String s = "2048-01-01" ;
//解析的格式要和字符串里时间的格式一样,否则会出现解析异常
SimpleDateFormat sdf = new simpleDateFormat ( "yyyy-MM-dd" );
Date date = sdf . parse ( s );
System . out . println ( date ); //Wed Jan 01 00:00:00 CST 2048
}
}
小结
格式化(从 Date
到 String
)
Public final String format(Date date) :把时间按照固定格式进行展示
解析(从 String
到 Date
)
Public Date parse(String source) :需要对时间进行计算
时间日期类-练习
案例:秒杀活动
秒杀开始时间:2020年11月11日 0:0:0
秒杀结束时间:2020年11月11日 0:10:0
小贾下单并付款的时间为:2020年11月11日 0:03:47
小皮下单并付款的时间为:2020年11月11日 0:10:11
用代码说明这两位同学有没有参加上秒杀活动?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class TestDemo {
public static void main ( String [] args ) {
String start = "2020年11月11日 0:0:0" ;
String end = "2020年11月11日 0:10:0" ;
String jia = "2020年11月11日 0:03:47" ;
String pi = "2020年11月11日 0:10:11" ;
SimpleDateFormat sdf = new simpleDateFormat ( "yyyyMMdd HH:mm:ss" );
long startTime = sdf . parse ( start ). getTime ();
long endTime = sdf . parse ( end ). getTime ();
long jiaTime = sdf . parse ( jia ). getTime ();
long piTime = sdf . parse ( pi ). getTime ();
if ( jiaTime >= startTime && jiaTime <= endTime ){
System . out . println ( "小贾同学参加上了秒杀活动" );
} else {
System . out . println ( "小贾同学没有参加上了秒杀活动" );
}
//---------------------------------------------
if ( piTime >= startTime && piTime <= endTime ){
System . out . println ( "小皮同学参加上了秒杀活动" );
} else {
System . out . println ( "小皮同学没有参加上了秒杀活动" );
}
}
}
Calendar
往前推 35
年是那天,往前推 10
个月是哪天,加多少天,减多少天,date
类解决不了
Calendar
:做表示日期以及日期计算
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 获取当前 年份、月份、日期
public static void getDateTime () throws ParseException {
Calendar now = Calendar . getInstance ();
System . out . println ( "年: " + now . get ( Calendar . YEAR ));
System . out . println ( "月: " + ( now . get ( Calendar . MONTH ) + 1 ) + "" );
System . out . println ( "日: " + now . get ( Calendar . DAY_OF_MONTH ));
System . out . println ( "时: " + now . get ( Calendar . HOUR_OF_DAY ));
System . out . println ( "分: " + now . get ( Calendar . MINUTE ));
System . out . println ( "秒: " + now . get ( Calendar . SECOND ));
System . out . println ( "当前时间毫秒数:" + now . getTimeInMillis ());
System . out . println ( now . getTime ());
Date d = new Date ();
System . out . println ( d );
SimpleDateFormat sdf = new SimpleDateFormat ( "yyyy-MM-dd HH:mm:ss" );
String dateNowStr = sdf . format ( d );
System . out . println ( "格式化后的日期:" + dateNowStr );
String str = "2012-1-13 17:26:33" ; //要跟上面sdf定义的格式一样
Date today = sdf . parse ( str );
System . out . println ( "字符串转成日期:" + today );
}
LocalDate-概述
旧版日期时间的问题
设计不合理,在 java.util
和 java.sql
的包中都有日期类,java.util.Date
同时包含日期和时间,而 java.sql.Date
仅仅包含日期,此外用于格式化和解析的类在 java.text
包下
非线程安全,java.util.Date
是非线程安全的,所有的日期类都是可变的,这是 java
日期类最大的问题之一
时区处理麻烦,日期类并不提供国际化,没有时区支持
新日期时间的API介绍
JDK8
中增加了一套全新的日期时间 API
,这套 API
设计合理,是线程安全的,新的日期及时间 API
位于 java.time
包中,下面是一些关键的类
名称
描述
LocalDate
表示日期,包含年月日,格式为 2019-10-16
LocalTime
表示时间,包含时分秒,格式为 16:38:54.158549300
LocalDateTime
表示日期时间,包含年月日,时分秒,格式为 2018-09-06T15:33:56.750
DateTimeFormatter
日期时间格式化类
Instant
时间戳,表示一个特定的时间瞬间
Duration
用于计算2个时间(LocalTime,时分秒)的距离
Period
用于计算2个日期(LocalDate,年月日)的距离
ZonedDateTime
包含时区的时间
LocalDate-使用
LocalDate使用
java.util.Date
和 SimpleDateFormatter
都不是线程安全的,而 LocalDate
和 LocalTime
和最基本的 String
一样,是不变类型,不单线程安全,而且不能修改。
java.util.Date
月份是从 0
开始,一月是 0
,十二月是 11
。
java.time.LocalDate
月份和星期都改成了 enum
,就不可能再用错了。
java.util.Date
包含日期、时间、还有毫秒数,在新的 java8
中,日期和时间被明确划分为 LocalDate
和 LocalTime
LocalDate
无法包含时间,LocalTime
无法包含日期,当然,LocalDateTime
才能同时包含时间和日期。
java.util.Date
推算时间(比如往前推几天、往后推几天、推算某年某月第一天等等)要结合 Calender
要写好多代码,相当麻烦,LocaDate
只需要使用对应的方法即可。
总结
Date
月份从 0
开始,容易用错,LocalDate
用的是枚举,不容易出错
Date
包含年月日时分秒,太乱了,LocalDate
将年月日时分秒分开了
Date
推算时间需要配合 Calender
使用,代码开发量大,LocalDate
直接使用对应的方法即可
获取当前时间
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Test
public void localDateCreate () {
LocalDate yyyyMMdd = LocalDate . now ();
LocalTime HHmmsssss = LocalTime . now ();
LocalDateTime yyyyMMddHHmmssSSs = LocalDateTime . now ();
System . out . println ( "年月日:" + yyyyMMdd );
System . out , println ( "时分秒毫秒:" + HHmmssSSS );
System . out . println ( "年月日时分秒毫秒:" + yyyyMMddHHmmssSSS ):
//输出:
//年月日:2020-10-16
//时分秒毫秒:09:55:49.448
//年月日时分秒毫秒:2020-10-16T09:55:49.448
}
设置自定义日期,根据指定日期、时间创建对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Test
public void setDate () {
LocalDate yyyyMMdd = LocalDate . of ( 2020 , 10 , 15 );
LocalTime HHmmssSSs = LocalTime . of ( 10 , 10 , 10 );
LocalDateTime yyyyMMddHHmmssSss = LocalDateTime . of ( 2020 , 10 , 15 , 10 , 10 );
System . out . println ( "自定义的年月日:" + yyyyMMdd );
System . out . print1n ( "自定义时分秒毫秒:" + HHmmssSSS );
System . out . println ( "自定义年月日时分秒毫秒:" + yyyyMMddHHmmssSSS );
//输出:
//自定义的年月日:2020-10-15
//自定义时分秒毫秒:10:10:10
//自定义年月日时分秒毫秒:2020-10-15T10:10
}
日期的加减
方法名
说明
public LocalDateTime plusYears(long years)
添加或者减去年
public LocalDateTime plusMonths(long months)
添加或者减去月
public LocalDateTime plusDays(long days)
添加或者减去日
public LocalDateTime plusHours(long hours)
添加或者减去时
public LocalDateTime plusMinutes(long minutes)
添加或者减去分
public LocalDateTime plusSeconds(long seconds)
添加或者减去秒
public LocalDateTime plusWeeks(long weeks)
添加或者减去周
调用 plusYears()
方法,括号中传入增加或减少的年数
当传入的参数为正数时,表示增加年份
当传入的参数为负数时,表示减少年份
方法名
说明
public LocalDateTime minusYears(long years)
减去或者添加年
public LocalDateTime minusMonths(long months)
减去或者添加月
public LocalDateTime minusDays(long days)
减去或者添加日
public LocalDateTime minusHours(long hours)
减去或者添加时
public LocalDateTime minusMinutes(long minutes)
减去或者添加分
public LocalDateTime minusSeconds(long seconds)
减去或者添加秒
public LocalDateTime minusWeeks(long weeks)
减去或者添加周
调用 minusYears()
方法,括号中传入增加或减少的年数,minus
与 plus
不同的是:
当传入的参数为正数时,表示减少年份
当传入的参数为负数时,表示增加年份
1
2
3
4
5
6
7
8
9
10
11
12
13
public static void main ( String [] args ) {
LocalDate yyyyMMdd = LocalDate . now ();
LocalDate addOneDay = yyyyMMdd . plusDays ( 1L ); //添加一日
LocalDate addOneMonths = yyyyMMdd . plusMonths ( 1L ); //添加一月
LocalDate addOneYears = yyyyMMdd . plusYears ( 1L ); //添加一年
LocalDate addOneWeeks = yyyyMMdd . plusWeeks ( 1L ); //添加一周
LocalDate delOneDay = yyyyMMdd . minusDays ( 1L ); //减去一日
LocalDate delOneMonths = yyyyMMdd . minusMonths ( 1L ); //减去一月
LocalDate delOneYears = yyyyMMdd . minusYears ( 1L ); //减去一年
LocalDate delOneWeeks = yyyyMMdd . minusWeeks ( 1L ); //减去一周
}
1
2
3
4
5
6
7
8
9
10
11
12
13
public static void main ( String [] args ) {
LocalTime HHmmssSSs = LocalTime . now ();
LocalTime addOneHours = HHmmssSSS . plusHours ( 1L ); //添加1小时
LocalTime addOneMinutes = HHmmssSSS . plusMinutes ( 1L ); //添加1分钟
LocalTime addOneSeconds = HHmmssSSS . plusSeconds ( 1L ); //添加1秒
LocalTime addOneNanos = HHmmssSSS . plusNanos ( 1L ); //添加1纳秒
LocalTime delOneHours = HHmmssSSS . minusHours ( 1L ); //减去1小时
LocalTime delOneMinutes = HHmmssSSS . minusMinutes ( 1L ); //减去1分钟
LocalTime delOneSeconds = HHmmssSSS . minusSeconds ( 1L ); //减去1秒
LocalTime delOneNanos = HHmmssSSS . minusNanos ( 1L ); //减去1纳秒
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static void main ( String [] args ) {
LocalDateTime yyyyMMddHHmmssSSs = LocalDateTime . now ();
LocalDateTime localDateTimeaddOneDay = yyyyMMddHHmmssSSS . plusDays ( 1L ); //添加一日
LocalDateTime localDateTimeaddOneMonths = yyyyMMddHHmmssSSS . plusMonths ( 1L ); //添加一月
LocalDateTime localDateTimeaddOneYears = yyyyMMddHHmmssSSS . plusYears ( 1L ); //添加一年
LocalDateTime localDateTimeaddOneWeeks = yyyyMMddHHmmssSSS . plusWeeks ( 1L ); //添加一周
LocalDateTime localDateTimeaddOneHours = yyyyMMddHHmmssSSS . plusHours ( 1L ); //添加1小时
LocalDateTime localDateTimeaddOneMinutes = yyyyMMddHHmmssSSS . plusMinutes ( 1L ); //添加1分钟
LocalDateTime localDateTimeaddOneSeconds = yyyyMMddHHmmsssss . plusseconds ( 1L ); //添加1秒
LocalDateTime localDateTimeaddOneNanos = yyyyMMddHHmmssSSS . plusNanos ( 1L ); //添加1纳秒
LocalDateTime localDateTimedelOneDay = yyyyMMddHHmmssSSS . minusDays ( 1L ); //减去一日
LocalDateTime localDateTimedelOneMonths = yyyyMMddHHmmssSSS . minusMonths ( 1L ); //减去一月
LocalDateTime localDateTimedelOneYears = yyyyMMddHHmmssSSS . minusYears ( 1L ); //减去一年
LocalDateTime localDateTimedelOneWeeks = yyyyMMddHHmmssSSS . minusWeeks ( 1I ); //减去一周
LocalDateTime localDateTimedelOneHours = yyyyMMddHHmmssSSS . minusHours ( 1L ); //减去1小时
LocalDateTime localDateTimedelOneMinutes = yyyyMMddHHmmsssss . minusMinutes ( 1L ); //减去1分钟
LocalDateTime localDateTimedelOneSeconds = yyyyMMddHHmmsssss . minusseconds ( 1L ); //减去1秒
LocalDateTime localDateTimedelOneNanos = yyyyMMddHHmmssSSS . minusNanos ( 1L ); //减去1纳秒
}
将年月日等修改为指定的值,并返回新的日期(时间)对象
方法名
说明
public LocalDateTime withYear(int year)
直接修改年
public LocalDateTime withMonth(int month)
直接修改月
public LocalDateTime withDayOfMonth(int dayofmonth)
直接修改日期(一个月中的第几天)
public LocalDateTime withDayOfYear(int dayOfYear)
直接修改日期(一年中的第几天)
public LocalDateTime withHour(int hour)
直接修改小时
public LocalDateTime withMinute(int minute)
直接修改分钟
public LocalDateTime withSecond(int second)
直接修改秒
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static void main ( String [] args ) {
LocalDate yyyyMMdd = LocalDate . now ();
System . out . println ( "LocalDate yyyyMMdd 当前时间:" + yyyyMMdd );
LocalDate addDay = yyyyMMdd . plusDays ( 4 );
System . out . println ( "LocalDate yyyyMMdd 添加4天后的日期:" + addDay );
LocalDate directDesignationDate = yyyyMMdd . withDayofMonth ( 20 );
System . out . println ( "LocalDate yyyyMMdd 直接指定到当月第几号:" + directDesignationDate );
LocalDate directDesignationYearDate = yyyyMMdd . withDayofYear ( 20 );
System . out . println ( "LocalDate yyyyMMdd 直接指定到当年第几天:" + directDesignationYearDate );
LocalDate directDesignationYear = yyyyMMdd . withYear ( 2000 );
System . out . println ( "LocalDate yyyyMMdd 当前时间直接指定年份:" + directDesignationYear );
LocalDate directDesignationMonth = yyyyMMdd . withMonth ( 6 );
System . out . println ( "LocalDate yyyyMMdd 当前时间直接指定月份:" + directDesignationMonth );
}
注意:在进行日期时间修改的时候,原来的 LocalDate
对象是不会被修改,每次操作都是返回了一个新的 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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public static void main ( String [] args ) {
LocalDateTime yyyyMMddHHmmssSss = LocalDateTime . now ();
//本年当中第多少天
int dayofYear = yyyyMMddHHmmsssss . getDayofYear ();
//本月当中第多少天
int dayofMonth = yyyyMMddHHmmsssss . getDayofMonth ();
//本周中星期几
DayofWeek dayofWeek = yyyyMMddHHmmsssss . getDayofweek ();
int value = dayofweek . getvalue ();
System . out . println ( "今天是" + yyyyMMddHHmmssSSS + "\n"
+ "本年当中第" + dayofYear + "天" + "\n"
+ "本月当中第" + dayofMonth + "天" + "\n"
+ "本周中星期" + value + "-即" + dayofWeek + "\n" );
//年
int year = yyyyMMddHHmmsssss . getYear ();
//月
Month month = yyyyMMddHHmmsssss . getMonth ();
//直接获取
int monthValue = yyyyMMddHHmmsssss . getMonthvalue ();
//日
int dayofMonth1 = yyyyMMddHHmmsssss . getDayofMonth ();
//时
int hour = yyyyMMddHHmmsssss . getHour ();
//分
int minute yyyyMMddHHmmsssss . getMinute ();
//秒
int second = yyyyMMddHHmmsssss . getsecond ();
//纳秒
int nano = yyyMMddHHmmsssss . getNano ();
System . out . println ( "今天是" + yyyyMMddHHmmssSSS + "\n"
+ "年:" + year + "\n"
+ "月:" + monthvalue + "-即" + month + "\n"
+ "日:" + dayofMonth1 + "\n"
+ "时:" + hour + "\n"
+ "分:" + minute + "\n"
+ "秒:" + second + "\n"
+ "纳秒:" + nano + "\n"
);
}
时间日期前后的比较与判断
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static void main ( String [] args ) {
LocalDate now = LocalDate . now ();
LocalDate of = LocalDate . of ( 2020 , 10 , 15 );
//判断of是否在now时间之前
boolean before = of . isBefore ( now );
System . out . println ( "判断of是否在now时间之前" + before );
//判断of是否在now时间之后
boolean after = of . isAfter ( now );
System . out . println ( "判断of是否在now时间之后" + after );
//判断两个时间是否相等
boolean equal = of . isEqual ( now );
System . out . println ( "判断两个时间是否相等" + equal );
//判断是否为闰年
boolean leapYear = now . isLeapYear ();
System . out . println ( "判断是否为闰年" + leapYear );
}
Java 8 时钟 clock
1
2
3
4
5
6
public static void main ( String [] args ) {
//返回当前时间,根据系统时间和UTC
Clock clock = Clock . systemUTC ();
System . out . println ( clock );
//运行结果:Systemclock[z]
}
时间戳
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 时间戳
* 事实上Instant就是java8以前的Date,
* 可以使用以下两个类中的方法在这两个类型之间进行转换,
* 比如 Date.from(Instant) 就是用来把Instant转换成java.util.date的,
* 而 new Date().toInstant() 就是将Date转换成Instant的
*/
@Test
public void instant () {
Instant instant = Instant . now ();
System . out . println ( instant );
Date date = Date . from ( instant );
Instant instant2 = date . toInstant ();
System . out . println ( date );
System . out . println ( instant2 );
}
在 JDK8
中给我们新增一个 Instant
类 (时间戳/时间线),内部保存了从 1970年1月1日 00:00:00
以来的秒和纳秒
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* Instant时间戳
* 可以用来统计时间消耗
*/
@Test
public void test7 () throws Exception {
Instant now = Instant . now ();
System . out . println ( "now =" + now );
//获取从1970年一月一日00:00:00到现在的纳秒
System . out . println ( now . getNano ());
Thread . sleep ( 5 );
Instant now1 = Instant . now ();
System . out . println ( "耗时:" + ( now1 . getNano () - now . getNano ()));
}
计算时间、日期间隔
使用 Period.between(开始时间,结束时间) 方法获取时间间隔的年月日
注:Period.between
中传入的参数是 LocalDate
型的对象,LocalDate
型只包含年月日
步骤:
先创建两个 LocalDate
对象
再使用 Period
调用 between
方法,传入两个 LocalDate
对象参数,再传给 Period
对象
再使用 period
对象调用获取相应时间的方法
使用 Duration.between(开始时间,结束时间) 方法获取时间间隔的秒、毫秒、纳秒:
注:Duration.between
中传入的参数是 LocalDateTime
型的对象,LocalDateTime
型包含年月日时分秒
步骤:
先创建两个 LocalDateTime
对象
再使用 Duration
调用 between
方法,传入两个 LocalDateTime
对象参数,再传给 Duration
对象
再使用 Duration
对象调用获取相应时间的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/**
* 计算时间、日期间隔
* Duration: 用于计算两个 "时间" 间隔
* Period: 用于计算两个 "日期" 间隔
*/
@Test
public void durationorPeriod () {
LocalDateTime now = LocalDateTime . now ();
System . out . println ( "duration当前时间:" + now );
LocalDateTime of = LocalDateTime . of ( 2020 , 10 , 15 , 10 , 24 );
System . out . println ( "duration自定义时间:" + of );
//Duration: 用于计算两个"时间"间隔
Duration duration = Duration . between ( now , of );
System . out . println ( now + "与" + of + "间隔" + "\n"
+ "天:" + duration . toDays () + "\n"
+ "时:" + duration . toHours () + "\n"
+ "分:" + duration . toMinutes () + "\n"
+ "毫秒:" + duration . toMillis () + "\n"
+ "纳秒:" + duration . toNanos () + "\n"
);
LocalDate nowDate = LocalDate . now ();
System . out . println ( "period当前时间:" + now );
LocalDate ofDate = LocalDate . of ( 2020 , 10 , 15 );
System . out . println ( "period自定义时间:" + of );
//Period:用于计两个"日期"间隔
Period period = Period . between ( nowDate , ofDate );
Sy3tem . out . printIn ( "Period相差年数:" + period . getYears ());
System . out . println ( "Period相差月数:" + period . getMonths ());
System . out . println ( "Period相差日数:" + period . getDays ());
//还可以这样获取相差的年月日
System . out . println ( "-----" );
long years = period . get ( ChronoUnit . YEARS );
long months = period . get ( ChronoUnit . MONTHS );
long days = period . get ( ChronoUnit . DAYS );
System . out . println ( "Period相差的年月日分别为:" + years + "," + months + "," + days );
//注意,当获取两个日期的间隔时,并不是单纯的年月日对应的数字相加减,而是会先算出具体差多少天,在折算成相差几年几月几日的
}
(图2
)
LocalDate-日期格式化
将时间 对象
转化为日期 字符串
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 将时间对象转化为日期字符串
*
时间日期的格式化(格式化后返回的类型是String) 自定格式 使用 DateTimeFormatter.ofPattern("yyyy-M-ddHH:mm:ss:SSS");
* 注:自定义转化的格式一定要与日期类型对应
* LocalDate只能设置仅含年月日的格式
* LocalTime只能设置仅含时分秒的格式
* LocalDateTime可以设置含年月日时分秒的格式
*/
@Test
public void formatterl () {
LocalDateTime now = LocalDateTime . now ();
DateTimeFormatter dtf = DateTimeFormatter . ofPattern ( "yyyy-MM-dd HH:mm:ss:SSS" );
String format = now . format ( dft );
System . out . println ( format );
//输出:2020-10-16 14:41:01:086
}
将时间 字符串
转化为日期 对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 将时间字符串形式转化为日期对象
* <p>
* 注:格式的写法必须与字符串的形式一样
* 2018-01-13 21:27:30 对应 yyyy-MM-dd HH:m:ss
* 20180113213328 对应 yyyyMMddHHmmss
* 否则会报运行时异常!
* <p>
* 但要记住:得到的最终结果都是类以2018-01-13T21:27:30的格式
* 因为在输出LocalDateTime对象时,会调用其重写的toString方法。
*/
@Test
public void formatter2 () {
String datestr = "2020-11-12" ;
DateTimeFormatter dtf = DateTimeFormatter . ofPattern ( "yyyy-MM-dd" );
LocalDate formatterDate = LocalDate . parse ( datestr , dtf );
System . out . println ( formatterDate );
//输出2020-11-12
}
将时间 日期对象
转化为 格式化后
的时间 日期对象
1
2
3
4
5
6
7
8
9
10
11
12
/**
* 将时间日期对象转为格式化后的时间日期对象
*/
@Test
public void formatter3 () {
//新的格式化API中,格式化后的结果都默认是String,有时我们也需要返回经过格式化的同类型对象
LocalDateTime ldt1 = LocalDateTime . now ();
DateTimeFormatter dtfl = DateTimeFormatter . ofPattern ( "yyyy-MM-dd HH:mm:ss" );
String temp = ldt1 . format ( dftl );
LocalDateTime formatedDateTime = LocalDateTime . parse ( temp , dftl );
System . out . println ( formatedDateTime );
}
总结
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 日期格式化
*/
@Test
public void test6 () {
LocalDateTime now = LocalDateTime . now ();
//指定格式 使用系统默认的格式 2021-05-27T16:16:38.139
DateTimeFormatter isoLocalDateTime = DateTimeFormatter . ISO_LOCAL_DATE_TIME ;
//将日期时间转换为字符串
String format = now . format ( isoLocalDateTime );
System . out . println ( "format =" + format );
//通过ofPattern方法来指定特定的格式
DateTimeFormatter dateTimeFormatter = DateTimeFormatter . ofPattern ( "yyyy-MM-dd HH:mm:ss" );
String format1 = now . format ( dateTimeFormatter );
//2021-05-27 16:16:38
System . out . println ( "format1 =" + format1 );
//将字符串解析为一个日期时间类型
LocalDateTime parse = LocalDateTime . parse ( "1997-05-06 22:45:16" , dateTimeFormatter );
// parse = 1997-05-06T22:45:16
System . out . println ( "parse =" + parse );
}
LocalDate-时间类型转换
ZoneOffset
表示 UTC
时区的 偏移量 ,而 ZoneId
表示 UTC
的 时区
UTC
偏移量是协调世界时(UTC
)和特定地点的日期与时间差异,其单位为小时和分钟
ZoneOffset.systemDefault()
:此方法返回系统默认时区的偏移量。换句话说,它返回的是相对于 UTC
的偏移量,以小时、分钟和秒的形式表示。例如,如果系统默认时区是 America/New_York
,那么返回的 ZoneOffset
将是 -04:56:02
。
ZoneId.systemDefault()
:此方法返回系统默认的时区 ID
。例如,如果系统默认时区是 America/New_York
,那么返回的 ZoneId
就会是 America/New_York
。
方式一
localDate
转 Date
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* localDate 转 date
* localDateTime 转 date
*/
@Test
public void localDateToDate () {
LocalDate now = LocalDate . now ();
Date from = Date . from ( now . atStartOfDay ( ZoneOffset . systemDefault ()). toInstant ());
LocalDateTime localDateTime = LocalDateTime . now ();
Date date = Date . from ( localDateTime . atZone ( ZoneOffset . ofHours ( 8 )). toInstant ()) ;
System . out . println ( from );
System . out . println ( date );
}
Date
转 localDate
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* date 转 localDate
* date 转 localDateTime
*/
@Test
public void dateToLocalDate () {
Date date = new Date ();
LocalDate localDate = date . toInstant (). atZone ( ZoneOffset . systemDefault ()). toLocalDate ();
System . out . println ( localDate );
LocalDateTime localDateTime = date . toInstant (). atZone ( ZoneOffset . systemDefault ()). toLocalDateTime ();
System . out . println ( localDateTime );
}
方式二
localDate
转 Date
1
2
3
4
5
6
public static void main ( String [] args ) {
LocalDate date = LocalDate . of ( 2006 , 07 , 26 );
ZoneId zone = ZoneId . systemDefault ();
Instant instant = date . atStartOfDay (). atZone ( zone ). toInstant ();
java . util . Date da = Date . from ( instant );
}
Date
转 localDate
1
2
3
4
5
6
public static void main ( String [] args ) {
Instant instant = new Date (). toInstant ();
ZoneId zone = ZoneId . systemDefault ();
LocalDateTime localDateTime = LocalDateTime . ofInstant ( instant , zone );
LocalDate localDate = localDateTime . toLocalDate ();
}
方式三
Date
转 localDate
1
new Date (). toInstant (). atZone ( ZoneId . systemDefault ()). toLocalDate ()
LocalDate
转 时间戳
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* LocalDate 转 时间戳
* LocalDateTime 转 时间戳
*/
@Test
public void localDateToInstant () {
LocalDate localDate = LocalDate . now () ;
long instant = localDate . atStartOfDay ( Zoneoffset . systemDefault ()). toInstant (). toEpochMilli ();
System . out . println ( instant );
LocalDateTime now = LocalDateTime . now ();
long instant1 = now . toInstant ( ZoneOffset . ofHours ( 8 )). toEpochMilli ();
System . out . println ( instant1 );
}
时间戳
转 localDate
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 时间戳 转 LocalDate
* 时间戳 转 LocalDateTime
*/
@Test
public void instantToLocalDate () {
long time = new Date (). getTime ();
LocalDateTime localDateTime = Instant . ofEpochMilli ( time ). atZone ( ZoneOffset . systemDefault ()). toLocalDateTime ();
System . out . println ( localDateTime );
LocalDate localDate = Instant . ofEpochMilli ( time ). atZone ( ZoneOffset . systemDefault ()) toLocalDate ();
System . out . println ( localDate );
}
LocalDate-时间校正器
有时候我们需要调整,将日期调整到 下个月的第一天
等操作。这时我们通过时间校正器效果可能会更好。
TemporalAdjuster
:时间校正器。
TemporalAdjusters
:通过该类静态方法提供了大量的常用 TemporalAdjuster
的实现。
(图3
)
LocalDate-日期时间的时区
Java8
中加入了对时区的支持,LocalDate
、LocalTime
、LocalDateTime
是不带时区的,带时区的日期时间类分别为:ZonedDate
、ZonedTime
、ZonedDateTime
。 其中每个时区都对应着 ID
,ID
的格式为 区域/城市
。例如:Asia/Shanghai
等。 ZoneId
:该类中包含了所有的时区信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static void main ( String [] args ) {
//1.获取所有的时区id
ZoneId . getAvailableZoneIds (). forEach ( System . out :: println );
//获取当前时间 中国使用的 东八区的时区,比标准时间早8个小时
LocalDateTime now = LocalDateTime . now ();
System . out . print1n ( "now=" + now ); //2021-05-27T17:17:06.951
//获取标准时间
ZonedDateTime bz = ZonedDateTime . now ( Clock . systemUTC ());
System . out . print1n ( "bz=" + bz ); //2021-05-27T09:17:86.952z
//使用计算机默认的时区,创建日期时间
ZonedDateTime now1 = ZonedDateTime . now ();
System . out . println ( "now1 =" + now1 ); //2021-05-27 T17:17:06.952 + 08:00[Asia Shanghai]
//使用指定的时区创建日期时间
ZonedDateTime now2 = ZonedDateTime . now ( ZoneId . of ( "America/Marigot" ));
System . out . println ( "now2 =" + now2 );
}
(图4
)
(图5
)
转换符
详细说明
示例
%s
字符串类型
“值得点赞”
%c
字符类型
‘h’
%b
布尔类型
true
%d
整数类型(十进制)
88
%x
整数类型(十六进制)
FF
%o
整数类型(八进制)
77
%f
浮点类型
7.777
%a
十六进制浮点类型
FF.35AE
%e
指数类型
9.38e+5
%g
通用浮点类型(f和e类型中较短的)
%h
散列码
%d%
百分比类型
%n
换行符
%tx
日期与时间类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public static void main ( String [] args ) {
String str ;
// %s
str = String . format ( "Hi,%s" , "布鲁斯" );
System . out . println ( str );
// %c %n
str = String . format ( "字母c的大写是:%c %n" , 'C' );
System . out . println ( str );
// %b
str = String . format ( "布尔结果是:%b" , 3 > 2 );
System . out . println ( str );
// %d
str = String . format ( "100的一半是:%d" , 100 / 2 );
System . out . println ( str );
// %x
str = String . format ( "100的16进制数是:%x" , 100 );
System . out . println ( str );
// %o
str = String . format ( "100的8进制数是:%o" , 100 );
System . out . println ( str );
// %f
str = String . format ( "50元的书打8.5折扣是:%f 元" , 50 * 0 . 85 );
System . out . println ( str );
// %a
str = String . format ( "上面价格的16进制数是:%a" , 50 * 0 . 85 );
System . out . println ( str );
// %e
str = String . format ( "上面价格的指数表示:%e" , 50 * 0 . 85 );
System . out . println ( str );
// %g
str = String . format ( "上面价格的指数和浮点数结果的长度较短的是:%g" , 50 * 0 . 85 );
System . out . println ( str );
// %d%
str = String . format ( "上面的折扣是:%d%%" , 85 );
System . out . println ( str );
// %h
str = String . format ( "字母A的散列码是:%h" , 'A' );
System . out . println ( str );
}
(图6
)
标识
说明
+
为正数或负数添加符号
0
在数字位数不够的地方补上0
空格
在位数不够的地方补上空格
,
对数字分组,三位一隔,只可以用于十进制
(
使用括号将去掉负号的负数包含进来
#
让十六进制的数字加上OX,八进制的数字加上o; 辅助%x和%o的使用,相当于一种对数字进制的补充说明提示
<
格式化前一个转换符锁描述的参数
-
左对齐,不够位数的地方补上空格
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public static void main ( String [] args ) {
// +
String str ;
str = String . format ( "数字的正负表示:%+d %d %+d %d" , 8 , 8 , - 8 , - 8 );
System . out . println ( str );
// -
str = String . format ( "左对齐:%-6d" , 8 );
System . out . println ( str );
// 0
str = String . format ( "缺位补零:%06d" , 8 );
System . out . println ( str );
// 空格
str = String . format ( "缺位补空格:% 6d" , 8 );
System . out . println ( str );
str = String . format ( "缺位补空格:% 6d" , - 8 );
System . out . println ( str );
// ,
str = String . format ( "数字分组:%,d" , 123456789 );
System . out . println ( str );
// (
str = String . format ( "括号用法:%(d" , - 8888 );
System . out . println ( str );
str = String . format ( "括号用法:%(d" , 8888 );
System . out . println ( str );
// #
str = String . format ( "#括号用法(十六进制):%#x" , 12 );
System . out . println ( str );
str = String . format ( "#括号用法(八进制):%#o" , 12 );
System . out . println ( str );
// <
str = String . format ( "<括号用法:%f %<3.1f" , 3 . 14 , 3 . 2 );
// "%<3.1f"作用的对象是前一个"%f"所作用的对象,3.1f
// f:代表接收的是浮点型参数;.1:保留一位小数;3:占三位字符 (包括小数点和小数部分)
//3.1 就是三位字符
System . out . println ( str );
}
(图7
)
标志
说明
c
包括全部日期和时间信息
F
“年-月-日"格式
D
‘月/日/年’格式
r
“HH:MM:SS PM”格式(12时制)
T
“HH:MM:SS”格式(24时制)
R
“HH:MM”格式(24时制)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public static void main ( String [] args ) {
String str ;
// c
str = String . format ( "全部日期和时间信息:%tc" , new Date ());
System . out . println ( str );
// F
str = String . format ( "年-月-日格式:%tF" , new Date ());
System . out . println ( str );
//D
str = String . format ( "月/日/年格式:%tD" , new Date ());
System . out . println ( str );
//r
str = String . format ( "HH:MM:SS PM格式(12时制):%tr" , new Date ());
System . out . println ( str );
//T
str = String . format ( "HH:MM:SS格式(24时制):%tT" , new Date ());
System . out . println ( str );
//R
str = String . format ( "HH:MM格式(24时制):%tR" , new Date ());
System . out . println ( str );
// %.2f保留两位小数
str = String . format ( "3.14保留两位小数:%.2f" , 3 . 141 );
System . out . println ( str );
}
(图8
)
集合概述-集合与数组的对比
场景:
有一个长度为 3
的数组,此时存储 4
个元素,前三个元素可以正常存放,第四个元素无法存放(这是因为数组的长度是不可变的)
有一个长度为 3
的集合,此时存储 4
个元素,前三个元素没有任何问题,但是还有第四个元素,此时,集合会进行自动扩容,从长度 3
变为长度 4
(集合长度可变)
数组可以存 基本数据类型 和 引用数据类型
集合只能 存引用数据类型 不能 存基本数据类型
如果集合 要存储基本数据类型 ,那么实际 存储的是他们的包装类
(图9
)
集合和数组的对比:
数组的长度是不可变的,集合的长度是可变的
数组可以存储基本数据类型和引用数据类型
集合只能存储引用数据类型,如果要存储基本数据类型,需要存对应的包装类
集合概述-集合体系结构
(图10
)
List
和 Set
都是接口,平时所说的 List
集合、Set
集合说的都是他们的实现类,而我们在实际创建对象时,创建的也是他们的实现类
mindmap
root((集合))
Collection: 单列集合顶层接口, 集合中一次只能存取一个元素
List: 有序,存储和遍历的顺序一致, 有索引, 元素可以重复
ArrayList: 1.底层结构是数组, 查询快, 增删慢; 2.线程不安全, 效率高;
LinkedList: 1.底层数据结构是链表, 查询慢, 增删快; 2.线程不安全, 效率高;
Set: 无序,存储和遍历的顺序不一致, 无索引, 元素不可以重复
HashSet: 底层是hash
LinkedHashSet: 底层是 链表+hash表, 是Set集合体系中唯一有序的集合
TreeSet: 底层是树形结构
Map: 双列集合顶层接口, 集合中一次存取一对元素, 元素必须成对出现
HashMap: 键的底层是hash表, HashMap的键就是HashSet
LinkedHashSet: 键的底层是 链表+hash表, 是Map集合体系中唯一有序的集合
LinkedHashMap: 键的底层是 链表+hash表, 是Map集合体系中唯一有序的集合
TreeMap: 键的底层是树形结构, TreeMap的键就是TreeSet
mindmap
root((集合))
Collection: 单列集合顶层接口, 集合中一次只能存取一个元素
List: 有序,存储和遍历的顺序一致, 有索引, 元素可以重复
ArrayList: 1.底层结构是数组, 查询快, 增删慢; 2.线程不安全, 效率高;
LinkedList: 1.底层数据结构是链表, 查询慢, 增删快; 2.线程不安全, 效率高;
Set: 无序,存储和遍历的顺序不一致, 无索引, 元素不可以重复
HashSet: 底层是hash
LinkedHashSet: 底层是 链表+hash表, 是Set集合体系中唯一有序的集合
TreeSet: 底层是树形结构
Map: 双列集合顶层接口, 集合中一次存取一对元素, 元素必须成对出现
HashMap: 键的底层是hash表, HashMap的键就是HashSet
LinkedHashSet: 键的底层是 链表+hash表, 是Map集合体系中唯一有序的集合
LinkedHashMap: 键的底层是 链表+hash表, 是Map集合体系中唯一有序的集合
TreeMap: 键的底层是树形结构, TreeMap的键就是TreeSet Collection-常见成员方法
Collection集合概述和使用
Collection集合概述
是单列集合的顶层接口,它表示一组对象,这些对象也称为 Collection
的元素
JDK
不提供此接口的任何直接实现,它提供更具体的子接口(如 Set
和 List
)实现创建 Collection
集合的对象
创建 Collection
集合的对象
Collection集合常用方法
方法名
说明
boolean add(E e)
添加元素
boolean remove(Object o)
从集合中移除指定的元素
boolean removeif(Object o)
根据条件进行删除
void clear()
清空集合
boolean contains(Object o)
判断集合中是否存在指定的元素
boolean isEmpty()
判断集合是否为空
int size()
集合的长度,也就是集合中元素的个数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
public class MyCollectonDemo {
public static void main ( String [] args ) {
//boolean add(E e) 添加元素
Collection < String > collection = new ArrayList <> ();
collection . add ( "aaa" );
collection . add ( "bbb" );
collection . add ( "ccc" );
//remove: 从集合中移除指定的元素,只会从集合中移除第一个匹配的元素
Collection < String > collection1 = new ArrayList <> ();
collection1 . add ( "a" );
collection1 . add ( "b" );
collection1 . add ( "b" );
collection1 . add ( "c" );
collection1 . remove ( "b" ); //[a, b, c]
}
//boolean removeif(object o) 根据条件进行删除
private static void method2 ( Collection < String > collection ) {
//removeif底层会遍历集合,得到集合中的每一个元素
//s依次表示集合中的每一个元素
//就会把这每一个元素都到lambda表达式中去判断一下
//如果返回的是true,则删除
//如果返回的是false,则保留不删除,
collection . removeIf (
( string s ) -> {
return s . length () == 3 ;
}
);
System . out . println ( collection );
//匿名内部类的实现方式
collection . removeIf ( new Predicate < String > (){
@Override
public boolean test ( String s ){
if ( s . length () == 3 ){
return true ;
}
});
}
//clear: 就是将集合中所有的元素全部删除。
private static void method3 ( Collection < String > collection ) {
collection . clear ();
System . out . println ( collection );
}
//boolean contains(object o) 判断集合中是否存在指定的元素
//contains: 拿着equals方法与集合里的元素一个一个比较
private static void method4 ( Collection < String > collection ) {
boolean result = collection . contains ( "a" );
System . out . println ( result );
boolean result2 = collection . contains ( "aaa" );
System . out . println ( result2 );
}
//boolean isEmpty() 判断集合是否为空
private static void method5 ( Collection < String > collection ){
boolean result = collection . isEmpty ();
System . out . println ( result );
}
//int size() 集合的长度,也就是集合中元素的个数
private static void method6 ( Collection < String > collection ) {
int size = collection . size ();
System . out . println ( size );
}
}
list.isEmpty() 与 CollectionUtils.isEmpty 的区别
CollectionUtils.isEmpty
的源码
1
2
3
public static boolean isEmpty ( @Nullable Collection <?> collection ) {
return collection == null || collection . isEmpty ();
}
isEmpty()
的源码
1
2
3
4
5
6
7
8
9
10
11
public boolean isEmpty () {
return size == 0 ;
}
public int size () {
return getArray (). length ;
}
final Object [] getArray () {
return array ;
}
很明显 list.isEmpty()
源码判空的时候,并没有对集合对象本身进行判空,在计算 size
值的时候可能出现空指针异常的情况,可以得出结论:当 list
为 null
的时候,list.isEmpty()
方法是会报空指针异常的,而 CollectionUtils.isEmpty()
方法则不会
也就是说下面的场景
1
2
3
4
5
6
7
8
//如果 list 为 null
if ( ! CollectionUtils . isEmpty ( list )) { //不会抛异常
//处理业务
}
if ( ! list . isEmpty ) { //会抛异常
//处理业务
}
Collection-迭代器基本使用
Collection集合的遍历
迭代器没有索引概念
一个迭代器只能使用一次,如果要进行第 2
次遍历,需再次创建迭代器
Iterator:迭代器,集合的专用遍历方式
Iterator iterator():返回集合中的迭代器对象,该迭代器对象默认指向当前集合的0索引
获取迭代器:
Iterator it = 集合对象.iterator();
Iterator的常用方法
Public Boolean hasNext()
:判断当前位置是否有元素可以被取出
Public E next()
:获取当前位置的元素,将迭代器对象移向下一个索引位置
Public void remove()
:删除当前正在遍历的元素
注意:
当使用迭代器进行遍历的时候,不允许对原始集合进行添加或删除操作
使用迭代器进行遍历的时候,如果非要删除,只能通过迭代器删除
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Demo {
public static void main ( String [] args ) {
Collection < string > list = new ArrayList <> ();
list . add ( "a" );
list . add ( "b" );
list . add ( "c" );
list . add ( "d" );
list . add ( "e" );
//1,获得迭代器的对象
//迭代器对象一旦被创建出来,默认指向集合的0索引处
Iterator < string > it = list . iterator ();
//利用迭代器里面的方法进行遍历
//当前位罩是否有元素可以被取出
System . out . println ( it . hasNext ());
//取出当前位置的元素 + 迭代器往后移动一个索引的位置
system . out . println ( it . next ())
System . out . println ( it . next ());
}
}
通过 while(true)
,只有当前位置有元素的时候,才可打印
1
2
3
while ( it . hasNext ()) {
system . out . println ( it . next ());
}
Collection-迭代器原理分析
(图11
)
(图12
)
(图13
)
(图14
)
(图15
)
迭代器小结:
Iterator<E> iterator()
:创建迭代器对象,默认指向当前集合的 0
索引。
boolean hasNext()
:判断当前位置是否有元素可以被取出
E next()
:获取当前位置的元素,将迭代器对象移向下一个索引位置
void remove()
:删除正在遍历的当前元素,并自动更新指针的位置
Collection-迭代器册除方法
练习:创建一个集合,有元素 a b b c d
使用循环遍历这个集合,判断当前获取到的元素是否为 b
,如果是就删除
原来的方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class MyCollectonDemo5 {
public static void main ( String [] args ) {
ArrayList < string > list = new ArrayList <> ();
list . add ( "a" );
list . add ( "b" );
list . add ( "b" );
list . add ( "c" );
list . add ( "d" );
for ( int i = 0 ; i < list . size (); i ++ ){
String s = list . get ( i );
if ( "b" . equals ( s )){
list . remove ( i ); //删除一个元素,后面的元素会往前移
i -- ;
}
}
System . out . println ( list );
}
}
现在的方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class MyCollectonDemo5 {
public static void main ( String [] args ) {
ArrayList < string > list = new ArrayList <> ();
list . add ( "a" );
list . add ( "b" );
list . add ( "b" );
list . add ( "c" );
list . add ( "d" );
Iterator < String > it = list . iterator ();
while ( it . hasNext ()){
String s = it . next ();
if ( "b" . equals ( s )){
//删除正在遍历的当前元素
it . remove ();
}
}
System . out . println ( list );
}
}
增强for-基本格式
增强for循环
增强 for
:简化数组和 collection
集合的遍历
它是 JDK5
之后出现的,其内部原理是一个 Iterator
迭代器
实现 Iterable
接口的类才可以使用迭代器和增强 for
增强 for
的格式:
1
2
3
for ( 元素数据类型 变量名 : 数组或者Collection集合 ){
//在此处使用变量即可,该变量就是元素
}
样例:
1
2
3
4
List < String > list = new ArrayList <> ();
for ( String s : list ){
System . out . println ( s )
}
s
是变量名,在循环的过程中,依次表示集合中的每一个元素,:
后面表示的是要遍历的集合或数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class MyCollectonDemo6 {
public static void main ( String [] args ) {
ArrayList < string > list = new ArrayList <> ();
list . add ( "a" );
list . add ( "b" );
list . add ( "c" );
list . add ( "d" );
list . add ( "e" );
list . add ( "f" );
//1,数据类型一定是集合或者数组中元素的类型
//2,Str仅仅是一个变量名而已,在循环的过程中,依次表示集合或者数组中的每一个元素
//3,list就是要遍历的集合或者数组。
for ( String str : list ){
System . out . println ( str );
}
}
}
增强for注意点
增强 for
遍历集合时,底层是迭代器
增强 for
遍历数组时,底层是普通 for
循环
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class MyCollectonDemo7 {
public static void main ( String [] args ) {
ArrayList < string > list = new ArrayList <> ();
list . add ( "a" );
list . add ( "b" );
list . add ( "c" );
list . add ( "d" );
for ( String str : list ){
str = "q" ;
}
System . out . println ( list ); //[a, b, c, d]
}
}
(图16
)
(图17
)
str
指的是第三方变量,遍历到那个元素,就把那个元素拿出来,给到第三方变量,所以,修改第三方变量中的值是不会影响到集合中的元素
两种方式生成增强 for
循环:
List.for回车
:快捷键生成增强 for
循环
List.iter
:快捷键生成增强 for
循环
三种循环的使用场景:
如果需要操作索引,使用普通 for
循环
如果在遍历的过程中需要删除元素,使用迭代器
如果仅仅想遍历,那么使用增强 for
Collection-练习
案例:Collection
集合存储学生对象并遍历
需求:创建一个 collection
集合存储学生对象,存储 3
个学生对象,使用程序实现控制台遍历该集合
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class MyCollectonDemo8 {
public static void main ( String [] args ) {
ArrayList < student > list = new ArrayList <> ();
Student s1 = new Student ( "小皮同学" , 23 );
Student s2 = new Student ( "小路同学" , 31 );
Student s3 = new Student ( "小贾同学" , 33 );
list . add ( s1 );
list . add ( s2 );
list . add ( s3 );
//迭代器的方式进行遍历
Iterator < Student > it = list . iterator ();
while ( it . hasNext ()){
Student s = it . next ();
System . out . println ( s );
}
//------------------------------------------
//增强for
for ( Student student : list ){
System . out . println ( student );
}
}
}
List概述和基本使用
(图18
)
List集合概述和特点
List
集合概述
有序集合,这里的有序指的是存取顺序
用户可以精确控制列表中每个元素的插入位置,用户可以通过整数索引访问元素,并搜索列表中的元素
与 set
集合不同,list
集合允许有重复的元素
List
集合特点
有序:存储和取出的元素顺序一致
有索引:可以通过索引操作元素
可重复:存储的元素可以重复
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class MyListDemo1 {
public static void main ( String [] args ) {
List < String > list = new ArrayList <> ();
list . add ( "aaa" );
list . add ( "bbb" );
list . add ( "ccc" );
Iterator < string > it = list . iterator ();
while ( it . hasNext ()){
String s = it . next ();
System . out . println ( s );
}
//-------------------------------------
for ( String s : list ){
System . out . println ( s );
}
}
}
List集合的特有方法
util 包下的
方法名
说明
void add(int index,E element)
在此集合中的指定位置插入指定的元素
E remove(int index)
删除指定索引处的元素,返回被删除的元素
E set(int index,E element)
修改指定索引处的元素,返回被修改的元素
E get(int index)
返回指定索引处的元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//void add(int index,E element)在此集合中的指定位置插入指定的元素
private static void method1 ( List < String > list ) {
//原来位置上的元素往后挪一个索引,
list . add ( 0 , "qqq" );
System . out . println ( list );
}
//E remove(int index) 删除指定索引处的元素,返回被删除的元素
private static void method2 ( List < String > list ) {
//在List集合中有两个删除的方法
//第一个删除指定的元素,返回值表示当前元素是否删除成功
//第二个删除指定索引的元素,返回值表示实际删除的元素
String s = list . remove ( 0 );
System . out . println ( s );
System . out . println ( list );
}
//E set(int index,E element) 修改指定索引处的元素,返回被修改的元素
private static void method3 ( List < String > list ) {
//被替换的那个元素,在集合中就不存在了·
string result = list . set ( "qqq" );
System . out . println ( result );
System . out . println ( list );
}
//E get(int index) 返回指定索引处的元素
private static void method3 ( List < String > list ) {
String s = list . get ( 0 );
System . out . println ( s );
}
List
方法和 ArrayList
的方法一样,ArrayList
是 List
的实现类,拥有 List
的方法
数据结构-栈和队列
数据结构
数据结构是计算机存储、组织数据的方法,是指相互之间存在一种或多种特定关系的数据元素的集合
通常情况下,精心选择的数据结构可以带来更高的运行或者存储效率
栈
队列
数组
链表
常见数据结构之栈
(图19
)
(图20
)
(图21
)
(图22
)
栈是一种数据 先进后出、后进先出 的模型
常见数据结构之队列
(图23
)
(图24
)
(图25
)
(图26
)
(图27
)
队列是一种数据 先进先出、后进后出 的模型
数据结构-数组和链表
常见数据结构之数组
(图28
)
数组是一种 查询快、增删慢 的模型
查询数据通过地址值和索引定位,查询任意数据耗时相同,查询速度快 (先通过数组的地址值找到容器,再通过索引找到元素)
删除数据时,要将原始数据删除,同时后面每个数据前移,删除效率低
添加数据时,添加位置后的每个数据后移,再添加元素,添加效率极低
常见数据结构之链表
(图29
)
添加一个数据 A
,再添加一个数据 C
,再添加一个数据 D
(图30
)
添加数据
在数据 AC
之间添加一个数据 B
① 数据 B
对应的下一个数据地址指向数据 C
② 数据 A
对应的下一个数据地址指向数据 B
(图31
)
删除数据
删除数据 BD
之间的数据 C
① 数据 B
对应的下一个数据地址指向数据 D
② 数据 C
删除
(图32
)
(图33
)
总结
链表是一种 增删快 的模型 (对比数组)
链表是一种 查询慢 的模型 (对比数组)
查询数据 D
是否存在,必须从头(head
)开始查询
查询第 N
个数据,必须从头(head
)开始查询
(图34
)
双向链表在查找元素时,他会判断这个元素离头近,还是离尾近
① 离头近,从头结点开始,一个一个往后找
② 离尾近,从尾结点开始,一个一个往前找
ArrayList源码解析
List集合常用实现类ArrayList
ArrayList
:底层数据结构是数组,查询快,增删慢
(图35
)
List<String> list = new ArrayList<>();
创建一个空间大小为 0
的数组
List.add("a");
当添加一个数据时,数组空间大小变为 10
(JDK1.8)
elementData
:ArrayList
底层的数组的名字
size
:是一个变量,默认指向 0
索引的位置,有两层含义,①表示下一次要操作的索引,②表示现在数组中元素的个数
例如:添加第一个元素 "a"
,把 "a"
添加到 size
指向的 0
索引,添加完毕后,size++
,此时 size
的值是 1
,表示我下一次添加第二个元素的时候,就存储到 1
索引的位置,现在集合的长度也为 1
(图36
)
集合添加数据
(图37
)
a、b、c、d
插入后,size
指向 4
索引,表示下一次再添加元素的时候,存储到 4
索引的位置,同时表示数组中有 4
个元素
(图38
)
(图39
)
(图40
)
当数组元素添加满后,集合会自动扩容,集合底层会再创建一个 1.5
倍长的数组
将老的数组中的元素拷贝到新的数组中,并且 size
的值不变,还是 10
集合查询数据
(图41
)
1
2
3
4
for ( int i = 0 ; i < list . size (); i ++ ) {
String s = list . get ( i );
System . out . println ( s );
}
因为集合的底层是数组,所以查询数据,通过索引和地址值
list.get(3);
也就是获取 elementDate
这个数组对应位置上的元素,只不过底层会做一个判断,如果获取的索引大于 size
就会报错
size()
方法底层返回的就是 size
变量的值
Arraylist底层源码分析
第一次添加数据时,底层的变化
(图42
)
oldCapacity + (oldCapacity >> 1) = 旧容量 * 1.5
详解:oldCapacity + oldCapacity / 2 = oldCapacity + oldCapacity * 1/2 = oldCapacity * (1 + 1/2) = oldCapacity * 1.5
return Math.max(DEFAULT_CAPACITY, minCapacity)
详解:Math.max(10, 1)
,添加第一个元素时,创建一个长度为 10
的数组
LinkedList-基本运用
LinkedList
(双向链表,没有索引,只有编号相当于计数器)
List集合常用实现类
List
集合的常用子类:ArrayList、LinkedList
ArrayList
:底层数据结构是数组,查询快,增删满
LinkedList
:底层是链表,查询慢,增删快
练习
使用 LinkedList
完成存储字符串并遍历
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class Test {
public static void main ( String [] args ) {
LinkedList < String > list = new LinkedList <> ();
list . add ( "aaa" );
list . add ( "bbb" );
list . add ( "ccc" );
//普通遍历
for ( int i = 0 ; i < list . size (); i ++ ){
System . out . println ( list . get ( i ));
}
//------------------------------------------------
//迭代器
Iterator < String > it = list . iterator ();
while ( it . hasNext ()){
String s = it . next ();
System . out . println ( s );
}
//------------------------------------------------
//增强for
for ( String s : list ){
System . out . println ( s );
}
}
}
LinkedList-特有功能
LinkedList集合的特有功能
方法名
说明
public void addFirst(E e)
在该列表开头插入指定的元素
public void addLast(E e)
将指定的元素追加到此列表的末尾
public E getFirst()
返回此列表中的第一个元素
public E getLast()
返回此列表中的最后一个元素
public E removeFirst()
从此列表中删除并返回第一个元素
public E removeLast()
从此列表中删除并返回最后一个元素