這個作業屬於哪個課程 | 2020春|S班(福州大學) |
---|---|
這個作業要求在哪里 | 作業要求 |
這個作業的目標 | 設計、開發一個疫情統計的程序、學習對程序的優化、學習GitHub的使用、PSP(個人軟件開發流程)的學習使用、《構建之法》的學習 |
作業正文 | https:////www.cnblogs.com/hhhqqq/p/12306200.html |
其他參考文獻 | 幾篇博客教程:GitHub|.gitignore|github desktop|fork、pr|JUnit4單元測試|性能分析|... |
GitHub倉庫地址
《構建之法》1~3章學習&PSP表格
學習記錄
PSP表格
PSP2.1 | Personal Software Process Stages | 預估耗時(min) | 實際耗時(min) |
---|---|---|---|
Planning | 計划 | 60 | 50 |
Estimate | 估計這個任務需要多少時間 | 10 | 15 |
Development | 開發 | 760 | 735 |
Analysis | 需求分析 (包括學習新技術) | 180 | 200 |
Design Spec | 生成設計文檔 | 20 | 30 |
Design Review | 設計復審 | 10 | 10 |
Coding Standard | 代碼規范 (為目前的開發制定合適的規范) | 40 | 30 |
Design | 具體設計 | 60 | 90 |
Coding | 具體編碼 | 300 | 290 |
Code Review | 代碼復審 | 30 | 25 |
Test | 測試(自我測試,修改代碼,提交修改) | 120 | 60 |
Reporting | 報告 | 90 | 150 |
Test Report | 測試報告 | 15 | 10 |
Size Measurement | 計算工作量 | 10 | 20 |
Postmortem & Process Improvement Plan | 事后總結, 並提出過程改進計划 | 45 | 25 |
total | 合計 | 990 | 1005 |
解題思路
對照“疫情統計程序的需求文檔”理清文檔的結構
221701419 \--src //源代碼 InfectStatistic.java Lib.java \--log //項目的輸入文本, 2020-01-22.log.txt ... \--result //處理后最終結果的輸出 ListOut1.txt ...
找到合適的數據結構
- 分析“日志文本”,需要統計的是每個省的數據,可以創建一個省類(Province)來記錄感染患者、疑似患者、治愈等信息
public class Province{ String provinceName; // 省份名稱 int ip; // 感染患者 int sp; // 疑似患者 int cure; // 治愈 int dead; // 死亡 //構造等其他方法 ... }
- 分析“需求”,最終的處理結果要將每個省的信息按行輸出,因此可以將之前參加統計的每個省類加入一個集合,輸出時從該集合中依次取出,並打印結果,我選擇了哈希表(HashTable),將省名作為鍵,Province作為值
Province Fujian = new Province(); HashTable<String, Province> hastable = new HashTable<String, Province>(35); hashtable.put("福建",Fujian);
對日志文本詳細分析
對log日志中出現的幾種情況分析,可以用String的split(" ")來分割主謂賓
該日志中出現的幾種情況 用split分割后的數組長度 1、<省> 新增 感染患者 n人 4 2、<省> 新增 疑似患者 n人 4 3、<省1> 感染患者 流入 <省2> n人 5 4、<省1> 疑似患者 流入 <省2> n人 5 5、<省> 死亡 n人 3 6、<省> 治愈 n人 3 7、<省> 疑似患者 確診感染 n人 4 8、<省> 排除 疑似患者 n人 4 String line = "福建 新增 感染患者 1人"; String[] afterSplitStrings = line.split(" "); //afterSplitStrings[0]:"福建" [1]:"新增" [2]:"感染患者" [3]:"1人"
分類后數組長度的三種情況:3、4、5
分類后可以得到需要修改的省份名稱,兩種情況:1.僅需修改一個省份;2.需修改兩個省份
得到需要修改的省份后判斷需要執行的操作類型,分析日志中出現的幾種情況思考要如何修改相應省份的數據,並將操作賦予一個類型ID
日志出現的情況 數據修改 操作類型ID <省> 死亡 n人 死亡數+n && 感染者-n 1 <省> 治愈 n人 治愈+n && 感染者-n 2 <省> 新增 感染患者 n人 感染者+n 3 <省> 新增 疑似患者 n人 疑似者+n 4 <省> 排除 疑似患者 n人 疑似者-n 5 <省> 疑似患者 確診感染 n人 疑似者-n && 感染者+n 6 <省1> 感染患者 流入 <省2> n人 省1 感染者-n && 省2 感染者+n 7 <省1> 疑似患者 流入 <省2> n人 省1 疑似者-n && 省2 疑似者+n 8 因此需要:1.Province對數據相應修改的方法
2.一個能夠根據省份名稱和操作類型ID執行相應操作的方法
對需求文檔詳細分析
先不考慮命令以及參數的內容,先將日期、輸入目錄、輸出文件作為常量使用,來完成需求里要求的功能:
- 讀取log文件夾里的文件
能夠篩選出指定日期前的文件
忽略注釋行
- 數據的統計
- 省份的統計
從輸入文件讀取一行后進行分析,省份存在以下情況:
只有一個省 *哈希表中沒有該省 *哈希表中存在該省 兩個省 *存在省1,存在省2 *存在省1,不存在省2 *不存在省1,存在省2 *不存在省1,不存在省2 ----存在則從hashtable取出,不存在則新建Province類,然后put進hashtable
- 全國的統計
所有要求的log日志文件處理完后,遍歷hashtable來統計全國的數據
- 輸出到相應目錄,全國總是排第一個,別的省按拼音先后排序,末尾填上一行文檔數據說名和一行命令
輸出示例: 全國 感染患者22人 疑似患者25人 治愈10人 死亡2人 福建 感染患者2人 疑似患者5人 治愈0人 死亡0人 浙江 感染患者3人 疑似患者5人 治愈2人 死亡1人 // 該文檔並非真實數據,僅供測試使用 // 命令: ....
命令行參數
一個命令list,五個參數名-log、 -out、 -date、 -type、 -province
示例命令行:
java InfectStatistic list -date 2020-01-22 -log D:/log/ -out D:/output.txt -type sp ip -province 福建
從list開始,其后的參數都存入了args數組,寫個方法從其中提取出參數名,然后再取得相應的參數值
比較需要注意的幾點:
-date 不設置為默認最新的一天,因此要有一個取得log里最新時間的方法;傳入的時間超過最新時間給出“日期超出范圍的提示”
-type 與 -province可能攜帶一到多個命令參數,-province指定輸出的省份也要排序,兩者的參數需要作為參數傳入給寫入文件的方法,對輸出的格式進行規約,可能出現以下四種組合:
1.指定類型 && 指定省份 2.指定類型 && 無指定省份 3.無指定類型 && 指定省份 4.無指定類型 && 無指定省份
設計實現過程
大致流程
graph LR a[命令行處理]-->b[初始化相應變量] b-->c[獲得log文件] c-->d[遍歷log統計數據] d-->e[打印結果到txt文件]
代碼組織
InfectStatistic的結構:
InfectStatistic{ public class Province{...} //省類,用來記錄感染患者、疑似患者、治愈、死亡等信息 static class ×××Methods{...} //存儲靜態方法的靜態類,編程過程中根據方法作用的類別歸類 public static void main(String[] args){} }
- 省類Province:
public class Province{ String provinceName; // 省份名稱 int ip; // 感染患者 int sp; // 疑似患者 int cure; // 治愈 int dead; // 死亡 //構造等其他方法 //屬性的加減 //輸出屬性信息 ... }
根據大致流程將靜態方法大致歸類:
- 對傳入的命令行的處理
- 取得log文件列表
- 統計信息
- 哈希表的相關操作
- 結果輸出
graph LR a[InfectStatistic]-->b[Province類] a-->c[靜態類-命令行處理相關方法] a-->d[靜態類-取得log文件相關方法] a-->e[靜態類-統計信息相關方法] a-->f[靜態類-哈希表操作的相關方法] a-->g[靜態類-打印結果相關方法]
關鍵函數流程圖
讀入信息,修改省份數據
graph LR A[一行信息]-->B[分割該字符串</br>得到省份&人數&操作類型] B-->C{哈希表中是否存在省} C-->|存在| D[取出該省] D-->E[修改數據] C-->|不存在| F[新建並初始化Province實例] F-->G[存入哈希表]獲取文件夾下指定日期前的所有文件名
graph TD a[格式化日期格式]-->b[用目錄下的文件名列表初始化字符串數組] b-->c[將字符串日期轉為Date格式] c-->d{指定的日期與列表中最大日期比較} d-->|指定日期大於最大日期|e[日期超出范圍提示] d-->|指定日期小於最大日期|f[遍歷日期字符串數組] f-->g{與指定日期比較} g-->|小於等於指定日期|h[加入結果數組] g-->|大於指定日期|i[無操作] j[編寫getMaxDate函數</br>獲得最大日期]-->d輸出文件
graph TD a[開始]-->b{判斷有無省份參數} b-->|沒有| e[排序傳入的哈希表] e-->f[遍歷該哈希表] f-->g{判斷有無類型參數} g-->|沒有|h[打印全部信息] g-->|有|i[打印指定信息] b-->|有|j[新建哈希表] j-->k[遍歷指定的省份] k-->L{判斷傳入的哈希表是否有該省} L-->|沒有| m[新建並初始化Province實例</br>傳入新的哈希表] L-->|有| o[從傳入的哈希表取出並傳入新的哈希表] m-->p[排序新的哈希表] o-->p p-->f
關鍵代碼說明
判斷操作類型
/** * description:判斷操作類型 * @param strings 分割后的字符串數組 * @return 返回值操作類型ID(1~8) */ public static int getOperateType(String[] strings) { int len = strings.length; int res = 0; if (len == 3) { if (strings[1].equals("死亡")) { res = 1; } else if (strings[1].equals("治愈")) { res = 2; } } else if (len == 4) { if (strings[1].equals("新增")) { if (strings[2].equals("感染患者")) { res = 3; } else if (strings[2].equals("疑似患者")) { res = 4; } } else if (strings[1].equals("排除")) { res = 5; } else { res = 6; } } else { if (strings[1].equals("感染患者")) { res = 7; } else { res = 8; } } return res; }
解釋思路:
根據讀取的每行信息分割后的數組,其長度只有三種:
- 3:
<省> 死亡 n人
或<省> 治愈 n人
,通過判斷第二個字符串區分操作類型;- 4:
<省> 新增 感染患者 n人
或<省> 新增 疑似患者 n人
或<省> 疑似患者 確診感染 n人
或<省> 排除 疑似患者 n人
,先判斷第二個字符串是“新增”還是“排除”,“新增”里再判斷第三個字符串是“感染患者”還是“疑似患者”,便可區分四者;- 5:
<省1> 感染患者 流入 <省2> n人
或<省1> 疑似患者 流入 <省2> n人
,判斷第二個字符即可
統計數據
/** * description:統計省份數據 * @param lineString 一行字符串 * @param hashtable 保存參與統計的省份 */ public static void calcProvince(String lineString, Hashtable<String, Province> hashtable) { InfectStatistic infectStatistic = new InfectStatistic(); String[] afterSplitStrings = lineString.split(" "); int numAfterSplit = afterSplitStrings.length; // 切割后數量 int number = OpLineStringMethods.getNumber(afterSplitStrings[numAfterSplit - 1]); // 一行信息中涉及的人數 String[] provinceNameStrings = OpLineStringMethods.getNeedModifyProvinceNames(afterSplitStrings); //需要修改數據的省份名稱 int operateType = OpLineStringMethods.getOperateType(afterSplitStrings); // 獲得操作類型 if (provinceNameStrings[1].equals("")) { // 只有一個省 if (!hashtable.containsKey(provinceNameStrings[0])) { // 哈希表中沒有該省 Province province = infectStatistic.new Province(provinceNameStrings[0], 0, 0, 0, 0); RelativeProviceMethods.executeOperate(province, province, operateType, number); hashtable.put(province.getProvinceName(), province); } else { Province province = hashtable.get(provinceNameStrings[0]); RelativeProviceMethods.executeOperate(province, province, operateType, number); } } else if (!provinceNameStrings[1].equals("")) { // 有兩個省 Province province1 = null; Province province2 = null; if (hashtable.containsKey(provinceNameStrings[0]) && hashtable.containsKey(provinceNameStrings[1])) { province1 = hashtable.get(provinceNameStrings[0]); province2 = hashtable.get(provinceNameStrings[1]); } else if (hashtable.containsKey(provinceNameStrings[0]) && !hashtable.containsKey(provinceNameStrings[1])) { province1 = hashtable.get(provinceNameStrings[0]); province2 = infectStatistic.new Province(provinceNameStrings[1], 0, 0, 0, 0); hashtable.put(provinceNameStrings[1], province2); } else if (!hashtable.containsKey(provinceNameStrings[0]) && hashtable.containsKey(provinceNameStrings[1])) { province1 = infectStatistic.new Province(provinceNameStrings[0], 0, 0, 0, 0); hashtable.put(provinceNameStrings[0], province1); province2 = hashtable.get(provinceNameStrings[1]); } else if (!hashtable.containsKey(provinceNameStrings[0]) && !hashtable.containsKey(provinceNameStrings[1])) { province1 = infectStatistic.new Province(provinceNameStrings[0], 0, 0, 0, 0); province2 = infectStatistic.new Province(provinceNameStrings[1], 0, 0, 0, 0); hashtable.put(provinceNameStrings[0], province1); hashtable.put(provinceNameStrings[1], province2); } RelativeProviceMethods.executeOperate(province1, province2, operateType, number); } } /** * description:統計全國的數據 * @param hashtable 保存着所有參與統計的省份 */ public static void calcWholeNation(Hashtable<String, Province> hashtable) { InfectStatistic infectStatistic = new InfectStatistic(); Province wholeNation = infectStatistic.new Province("全國", 0, 0, 0, 0); Set set = hashtable.keySet(); Iterator iterator = set.iterator(); while(iterator.hasNext()) { Object keyObject = iterator.next(); wholeNation.ip += hashtable.get(keyObject).getIp(); wholeNation.sp += hashtable.get(keyObject).getSp(); wholeNation.cure += hashtable.get(keyObject).getCure(); wholeNation.dead += hashtable.get(keyObject).getDead(); } hashtable.put("全國", wholeNation); }
解釋思路:
統計省份數據函數
:傳入從log文件讀取的一行,切割取得數組,獲得需要修改數據的省份、人數數量以及操作類型,然后判別省份個數(1個|2個),進而判別哈希表中是否存在該省份,如果存在,說明該省前面已經統計過部分數據,所以從哈希表中取出;如果不存在,則創建一個Province類;接着將省份、人數數量、操作類型傳入執行操作的靜態方法executeOperate(),執行相應的操作;操作完成后,之前新建的Province類要put進哈希表。
統計全國數據
:統計完所有日志文檔后,新建一個全國的Province實例wholeNation,遍歷哈希表,累計各項屬性的值,賦給wholeNation的相應屬性,再將wholeNation存入哈希表
寫入文件
/** * description:寫入文件 * @param hashtable 保存着所有參與統計的省份 * @param fileOutputStream 輸出文件流 * @param paramenterOfType數組 -type的參數值 * @param paramenterOfProvice數組 -province的參數值 * @param commandLineStrings數組 命令行數組 argv */ public static void writeFile(Hashtable<String, Province> hashtable, FileOutputStream fileOutputStream, String[] paramentersOfType, String[] paramentersOfProvince,String[] commandLineStrings) { String endLineString = "// 該文檔並非真實數據,僅供測試使用"; String commandLineString = "// 命令:"; for(int i=0; i<commandLineStrings.length; i++) { commandLineString = commandLineString + commandLineStrings[i] + " "; } InfectStatistic infectStatistic = new InfectStatistic(); Province wholeNation = hashtable.get("全國"); try { OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream,"UTF8"); if(paramentersOfProvince[0].equals("null")) { //沒有指定省份 Set set = hashtable.keySet(); Iterator iterator = set.iterator(); List<Map.Entry<String,Province>> list = OpHashTableMethods.sortByHeadAlphabet(hashtable); //排序 for (Map.Entry entry : list){ Province province = (Province) entry.getValue(); if(paramentersOfType[0].equals("null")) { //沒有指定輸出類型 outputStreamWriter.write(province.getAllResult() + "\r\n"); outputStreamWriter.flush(); }else { outputStreamWriter.write(province.getResultByRequest(paramentersOfType) + "\r\n"); outputStreamWriter.flush(); } } outputStreamWriter.write(endLineString + "\r\n" + commandLineString); outputStreamWriter.flush(); }else { //指定省份 Hashtable<String, Province> requestProvinceHashtable = new Hashtable<String, InfectStatistic.Province>(); // for(int i=0; i<paramentersOfProvince.length; i++) { // 別用.length,指定的省的個數不一定等於數組的大小 for(int i=0; paramentersOfProvince[i] != null; i++) { if(!hashtable.containsKey(paramentersOfProvince[i])) { //哈希表中不存在 Province province = infectStatistic.new Province(paramentersOfProvince[i], 0, 0, 0, 0); requestProvinceHashtable.put(paramentersOfProvince[i], province); }else { //哈希表中存在 Province province = hashtable.get(paramentersOfProvince[i]); requestProvinceHashtable.put(paramentersOfProvince[i], province); } } List<Map.Entry<String,Province>> list = OpHashTableMethods.sortByHeadAlphabet(requestProvinceHashtable); //排序 for (Map.Entry entry : list){ Province province = (Province) entry.getValue(); if(paramentersOfType[0].equals("null")) { //沒有指定輸出類型 outputStreamWriter.write(province.getAllResult() + "\r\n"); outputStreamWriter.flush(); }else { outputStreamWriter.write(province.getResultByRequest(paramentersOfType) + "\r\n"); outputStreamWriter.flush(); } } outputStreamWriter.write(endLineString + "\r\n" + commandLineString); outputStreamWriter.flush(); } } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } }
解釋思路:
主要是判斷用戶是否傳入了-type和-province,有四種組合,不過該方法里主要判別province,有無type只要通過調用Province不同的方法來輸出不同的結果即可,所以主要兩大類:
- 沒有指定省份
先對哈希表排序,然后遍歷哈希表,再判別有無-type,調用Province的方法打印該省的相應數據
- 指定了省份
新建一個保存指定省份信息的哈希表requestProvinceHashtable,遍歷指定的所有省份,判斷傳入的哈希表hashtable(即保存着在log中出現的所有省份的數據)中是否存在當前的省(指定輸出的省份可能沒在log文件中出現),如果不存在,新建該省份的Province實例,並加入requestProvinceHashtable,如果存在,從hashtable中取出該省,並加入requestProvinceHashtable,因此requestProvinceHashtable中保存了想要輸出的省份的數據,然后排序requestProvinceHashtable,再遍歷輸出
Province的兩個獲得結果的方法:
/** * description:打印全部統計的數據結果 * @return resString 返回值為字符串 */ public String getAllResult() { String resString = provinceName + " " + "感染患者" + ip + "人" + " " + "疑似患者" + sp + "人" + " " + "治愈" + cure + "人" + " " + "死亡" + dead + "人"; return resString; } /** * description:按指定參數值要求給出結果 * @param paramenterOf 一個保存着-type的參數值的數組 * @return resString 返回值為字符串 */ public String getResultByRequest(String[] paramentersOfType) { String resString = provinceName + " "; for(int i=0; paramentersOfType[i] != null; i++) { switch (paramentersOfType[i]) { case "ip": resString += "感染患者" + " " + ip + "人" + " "; break; case "sp": resString += "疑似患者" + " " + sp + "人" + " "; break; case "cure": resString += "治愈" + " " + cure + "人" + " "; break; case "dead": resString += "死亡" + " " + dead + "人" + " "; break; default: break; } } return resString; }
命令行的處理
HashMap<Integer, String> paramenterHashMap = new HashMap<Integer, String>(5); paramenterHashMap.put(1, "-log"); paramenterHashMap.put(2, "-out"); paramenterHashMap.put(3, "-date"); paramenterHashMap.put(4, "-type"); paramenterHashMap.put(5, "-province"); String[] paramenterStrings = new String[args.length - 1]; //存儲傳入的參數名、參數值 for(int i=1; i<args.length; i++) { paramenterStrings[i-1] = args[i]; } int[] indexOfParamenterStrings = {-1, -1, -1, -1, -1, -1}; //找到參數名,並記錄位置 for(int i=0; i<paramenterStrings.length; i++) { int key = OpHashTableMethods.getKey(paramenterHashMap, paramenterStrings[i]); if( key != -1) { //是參數名 indexOfParamenterStrings[key] = i; //key對應的參數名在patamenterStrings的i下標位置,值為-1則代表無此參數名 } } /** * 初始化輸入路徑、輸出路徑、截至日期、type參數值、province參數值 */ String directoryString = "./log"; // log 日志文件目錄,項目必會附帶,如果沒有,從項目里的log取 String outputFileNameString = "./result/testOutput.txt"; //輸出路徑/文件名 String toDateString = GetFileMethods.getToday(); //統計到哪一天 String[] paramentersOfType = new String[10];; //type的參數值 String[] paramentersOfProvince = new String[25]; //province的參數值 paramentersOfType[0] = "null"; paramentersOfProvince[0] = "null"; //接着處理每個參數名對應的參數值 for(int i=1; i<=5; i++) { if(indexOfParamenterStrings[i] != -1) { //傳入了該參數名 if(i == 1) { // -log directoryString = paramenterStrings[indexOfParamenterStrings[i] + 1]; //配置log路徑 }else if(i == 2) { //-out outputFileNameString = paramenterStrings[indexOfParamenterStrings[i] + 1]; //配置輸出文件路徑 }else if(i == 3) { //-date toDateString = paramenterStrings[indexOfParamenterStrings[i] + 1]; //統計到哪一天 }else if(i == 4) { //-type 可能會有多個參數 String[] paramenterValues = new String[20]; //記錄所有參數值 int cnt = 0; //取得參數值,直到找到下一個參數名時停止, 當前參數名 參數值1 參數值2 ... 下一個參數名 for(int j=indexOfParamenterStrings[i]+1; j<paramenterStrings.length && OpHashTableMethods.getKey(paramenterHashMap, paramenterStrings[j])==-1; j++) { paramenterValues[cnt++] = paramenterStrings[j]; paramentersOfType = paramenterValues; } }else if(i == 5) { //-province String[] paramenterValues = new String[20]; int cnt = 0; //取得參數值,直到找到下一個參數名時停止, 當前參數名 參數值1 參數值2 ... 下一個參數名 for(int j=indexOfParamenterStrings[i]+1; j<paramenterStrings.length && OpHashTableMethods.getKey(paramenterHashMap, paramenterStrings[j])==-1; j++) { paramenterValues[cnt++] = paramenterStrings[j]; paramentersOfProvince = paramenterValues; } } } }
解釋說明:
用五個參數名初始化一個hashMap,用傳入的參數名、參數值初始化一個字符串數組paramenterStrings,用-1初始化一個大小為6的int數組indexOfParamenterStrings,遍歷paramenterStrings,如果在hashMap中存在該值(該值為參數名),則將該值在hashMap中對應的鍵作為indexOfParamenterStrings的下標,將該值對應paramenterStrings的下標作為indexOfParamenterStrings的值
例:indexOfParamenterStrings[4] = 6 代表的是hashMap中鍵為4的參數名-type在paramenterStrings[6]中 indexOfParamenterStrings[4] = -1 則代表沒有傳入該參數
然后從下標1開始遍歷indexOfParamenterStrings(初始化hashMap時從1開始的),判斷是否傳入了該參數名,如果存在,從paramenterStrings中為當前參數名的下一個位置開始取得參數值,直到paramenterStrings的盡頭或者遇到下一個參數名,然后用取得的參數值初始化相應的變量
單元測試截圖和描述
獲取字符串前的數字
測試用例
測試結果:
獲得需要修改數據的省份
測試數據:
public String[] testStrings = { "福建 新增 感染患者 2人" , "福建 新增 疑似患者 5人" , "湖北 新增 感染患者 15人" , "湖北 新增 疑似患者 20人" , "湖北 感染患者 流入 福建 2人" , "湖北 疑似患者 流入 福建 3人" , "湖北 死亡 1人" , "湖北 治愈 2人" , "福建 疑似患者 確診感染 1人" , "湖北 排除 疑似患者 2人" };
測試用例:
測試結果:
判斷操作類型
測試用例:
測試結果:
獲得最大日期
測試用例:
測試結果:
獲得指定日期前的所有文件
測試用例:
測試結果:
按城市首字母排序,“全國”置頂
測試用例:
測試結果:
統計省份數據
測試數據:
public String[] testStrings = { "福建 新增 感染患者 2人" , "福建 新增 疑似患者 5人" , "湖北 新增 感染患者 15人" , "湖北 新增 疑似患者 20人" , "湖北 感染患者 流入 福建 2人" , "湖北 疑似患者 流入 福建 3人" , "湖北 死亡 1人" , "湖北 治愈 2人" , "福建 疑似患者 確診感染 1人" , "湖北 排除 疑似患者 2人" };
測試用例:
測試結果:
統計全國的數據
測試用例:
測試結果:
寫入文件
測試用例:
測試結果:
HashMap根據value獲取key
測試用例:
測試結果:
單元測試覆蓋率優化和性能測試
覆蓋率測試
application覆蓋測試
JUnit Test覆蓋測試
性能測試
總覽
內存情況
CPU
Live memory-classes
優化過程--性能優化&提升覆蓋率
性能優化
1.提取尋循環中可以重用的對象到循環外。在循環中new對象,不僅要花時間來創建,還要花時間對這些對象進行垃圾回收和處理
for(int i=0; i<nameStrings.length; i++) { Date tmpDate = dFormat.parse(nameStrings[i]); //... } 改成 ↓ Date tmpDate = new Date(); for(int i=0; i<nameStrings.length; i++) { tmpDate = dFormat.parse(nameStrings[i]); //... }
2.減少變量的重復計算,避免在循環條件中使用復雜的表達式
for(int i=0; i<nameStrings.length; i++) {...} 改成 ↓ for(int i=0, len=nameStrings.length; i<len; i++) {...}
3.將try/catch語句放到循環最外層
for(...){ try{ /... }catch{ } } 改成 ↓ try{ for(...){} }catch{ }
4.用散列值取出相應的Entry,來遍歷哈希表
List<Map.Entry<String,Province>> list = OpHashTableMethods.sortByHeadAlphabet(hashtable); //排序 for (Map.Entry entry : list){ province = (Province) entry.getValue(); //... }
5.字符串相加時,如果只有一個字符,用' ',不用“ ”
更改完代碼后性能檢測:
提升代碼覆蓋率
1.刪掉沒用的代碼,剛開始寫了,但是后來沒用到的函數等
示例:
2.改變判斷結構
示例:
修改后:
3.消除重復代碼
示例:
修改后:
調整完覆蓋率測試
優化結果
性能優化
覆蓋率提升
代碼規范
https://github.com/904566722/InfectStatistic-main/blob/master/221701419/codestyle.md
心路歷程&收獲
這次的作業相較於第一次,量還是比較多的,看完第一遍之后的感受就是有許多不知道的東西,PSP、單元測試等等,觸及到了我的知識盲區...然后就決定先不管,先看看《編程之法》前三章,里面提到了許多問題都是自己目前存在的,粗略列幾點來提醒自己:
- 技止此耳? 看書的時候多思考此項技術及延伸,及時實踐發現同書上的不同,技術更新總是很快
- 唯手熟爾。 不斷練習,把低層次常遇到的問題變成大腦的“自動操作”,才能做更高層次的隨機應變
- 多測試結果。多對寫完的程序進行測試,多找Bug,寫更好的軟件、程序。寫完程序我總是很懶得去測試...
- ...
寫到這里了,這次任務就快要結束了,通過這次的學習還是收獲到不少,包括舊知識的復習、新知識的學習,這次的任務涉及到了很多的知識跟技術,Java、GitHub、PSP、單元測試等等,有一部分都是之前了解過、但不怎么使用的,通過這次的學習,回顧了Java代碼的編寫,進一步了解了GitHub、markdown的一些使用技巧,學習到了PSP、單元測試、覆蓋率等等新的知識,以及很重要的對自我的反省,還是很充實的,收獲很多。
Android學習相關的5個倉庫
1.AndroidAllGuide
鏈接:https://github.com/904566722/AndroidAllGuide
簡介: 這是一份關於 Java、Kotlin、Dart、Android 、Flutter 的學習指南 , 本指南以 Java & Kotlin & Dart 的基礎語法知識作為開始,涵蓋了大部分的語言知識點,幫助初學者入門
2.Android
鏈接:https://github.com/itheima1/Android
簡介: 收集Android方方面面的經典知識, 最新技術. 涵蓋Android方方面面的技術, 目前保持更新. 時刻與Android開發流行前沿同步.
3.BGAPhotoPicker-Android
鏈接:https://github.com/bingoogolapple/BGAPhotoPicker-Android
簡介: Android 圖片選擇、預覽、九宮格圖片控件、拖拽排序九宮格圖片控件
4.DDComponentForAndroid
鏈接:https://github.com/luojilab/DDComponentForAndroid
簡介: 一套完整有效的android組件化方案,支持組件的組件完全隔離、單獨調試、集成調試、組件交互、UI跳轉、動態加載卸載等功能
5.Coder
鏈接:https://github.com/CoderGuoy/Coder
簡介: 項目使用MVVM模式進行開發, Tablayout | 橫向布局標簽,TextInputLayout | 文字輸入布局 ,FloatingActionButton | 懸浮按鈕 等