可能大部分同窗都據說過一個知名的故事。一位小學老師,爲了讓同窗們中止吵鬧,給出了一道數據題 1+2+3+…+100 = ? 本來覺得可讓他們安靜二三十分鐘,結果1分鐘不到,就有一個小朋友舉手回答了出來,老師漫不經心的看了一眼答案,萬萬沒想到居然是正確的。api
這個小朋友只有9歲,他就是高斯。函數
高斯有個好習慣:不管如何都要記日記。他的日記有個不同凡響的地方,他從不註明年月日,而是用一個整數代替,好比:4210工具
後來人們知道,那個整數就是日期,它表示那一天是高斯出生後的第幾天。這或許也是個好習慣,它時時刻刻提醒着主人:日子又過去一天,還有多少時光能夠用於浪費呢?源碼分析
高斯出生於:1777年4月30日。在高斯發現的一個重要定理的日記上標註着:5343,所以可算出那天是:1791年12月15日。高斯得到博士學位的那天日記上標着:8113 。如今請算出高斯得到博士學位的年月日?測試
咱們來抽象一下問題,本質上是要求取某年某月某往後的n天,是哪年哪月哪日?this
方案一:循環遍歷法spa
比較容易想到的就是遍歷的方式,循環n次,就能夠推導出n天后的日期了,咱們來嘗試一下。設計
public static void calc(int year, int month, int day, int n) { for (int i = 0; i < n; i++) {
// 日子一每天過 day++;
// 若是過到月底,須要把日期重置,月份+1 if(day > getMonthLastDay(year,month)){ day = 1; month++;
// 若是過到年末,須要把月份和日期同時重置,年份+1 if(month >12){
month = 1; day = 1; year++; } } } System.out.println( n + "天后的日期爲:"+year + "-" + month + "-" +day); }
先把已知條件帶入,在main函數中執行 calc(1777,4,30,5343); code
能夠獲得結果爲1791年12月16日,和題目上的1791年12月15日相差一天,這是什麼緣由呢?blog
細想能夠察覺,高斯生日的這一天其實被算做了第一天的,也就是咱們帶入方法時,須要傳入1777年4月29日
calc(1777,4,29,5343); 的運行結果是 1791年12月15日
而 calc(1777,4,29,8113); 的運行結果是 1799年7月16日,能夠想見高斯在22歲時就已得到博士學位了,真正的年少得志。
方案二:善用工具法
jdk8給咱們提供了一系列很友好的日期api,讓咱們求解此類問題時,能夠快速拿到答案。
// 建立一個高斯生日前一天的日期
LocalDate date = LocalDate.of(1777,4,29);
// 調用增長天數的方法,可以直接得到n天后的日期 System.out.println(date.plusDays(8113));
既然有咱們本身來書寫的方案,還有jdk提供的方案,那麼問題來了,哪一種方案執行更快、效率更好呢?
long start = System.currentTimeMillis();
calc(1777, 4, 29, 8113);
long cost = System.currentTimeMillis() - start;
System.out.println("耗時" + cost);
System.out.println("===========");
start = System.currentTimeMillis();
//日期工具 jdk8 joda time
LocalDate date = LocalDate.of(1777, 4, 29);
System.out.println(date.plusDays(8113));
cost = System.currentTimeMillis() - start;
System.out.println("耗時" + cost);
咱們經過毫秒計算工具來測試一下,獲得結果以下
注意,這裏的時間單位是ms,兩種方案實際相差爲0.1s左右。
大跌你的眼鏡吧,咱們本身寫的實現居然比jdk提供的實現方式快,這是爲何呢?
咱們深刻LocalDate類的實現看一下,和咱們本身實現的有何區別?
public LocalDate plusDays(long daysToAdd) { if (daysToAdd == 0) { return this; }
// addExact就是一個簡單的加法運算
// toEpochDay() 其中epoch表明的是元年,計算機元年是1970年1月1日,這裏計算的是當前日期距離元年的天數
// 當前日期距離元年的天數 + 當前日期事後的n天 = n天后距離元年的天數 long mjDay = Math.addExact(toEpochDay(), daysToAdd);
// ofEpochDay(int n) 是計算距離元年n天的日期 return LocalDate.ofEpochDay(mjDay); }
再點擊進 toEpochDay() 看一下
public long toEpochDay() { long y = year; long m = month; long total = 0; total += 365 * y; if (y >= 0) { total += (y + 3) / 4 - (y + 99) / 100 + (y + 399) / 400; } else { total -= y / -4 - y / -100 + y / -400; } total += ((367 * m - 362) / 12); total += day - 1; if (m > 2) { total--; if (isLeapYear() == false) { total--; } } return total - DAYS_0000_TO_1970; }
能夠看到,這個方法都是經過公式來計算的,而 ofEpochDay() 也一樣如此,因此咱們能夠簡單把工具法等價爲公式法。
爲何循環法比公式法還快呢,咱們拉長一下這個問題。當要計算更多天數以後的日期時,二者的表現如何呢?
咱們計算 8113333 天后的日期,能夠發現的循環法耗時和公式法耗時基本一致,都在0.1s左右。再增長到81133333 天后呢,能夠明顯的看到公式法仍然保持在0.1s,而循環法的耗時變爲1s以上,大幅提升。
這說明在計算次數增長以後,循環法的耗時會成斜線增加,而公式法的耗時基本保持在水平直線上,這也是jdk的設計者們所作的權衡。對咱們自身來說,不一樣的應用場景下,能夠選擇不一樣的實現方式,沒有最好的,只有最適合的,你get到了嗎?