第4周作业:WordCount 优化
1. GitHub地址
https://github.com/llag9810/Software-Quality-and-Testing-HW2
2. PSP表格
PSP阶段 | 预计耗时(分钟) | 实际耗时(分钟) |
计划 | 2h | 1h |
.估计这个任务需要时间 | 5h | 3h |
开发 | 8h | 8h |
.需求分析(包括学习新技术) | 1h | 1h |
.生成设计文档 | 1.5h | 1.5h |
.设计复审 | 0.5h | 0.5h |
.代码规范 | 1h | 1h |
.具体设计 | 0.5h | 0.5h |
.具体编码 | 2h | 2h |
.代码复审 | 1 | 1h |
.测试 | 1h | 1h |
报告 | 2h | 2h |
.测试报告 | 0.5h | 0.5h |
.计算工作量 | 0.2h | 0.2h |
.事后总结并提出改进计划 | 0.3h |
0.3h |
合计 | 10h |
10h |
3. 代码实现
本程序分为三个部分:
Main.java:程序的入口,用于读文件、完成参数有效性判断的功能。
Counter.java:计数类。用于计数,并维护计数过程中 Top100 单词。
Output.java:专用于输出的类。
程序设计思路:
1. 使用正则表达式来匹配合法单词。
Pattern pattern = Pattern.compile("[A-Za-z]+(-[A-Za-z]*)?"); String line = scanner.nextLine(); Matcher matcher = pattern.matcher(line);
2. 使用 HashMap 和最小堆来维护出现频率前100的单词。
HashMap的key是单词,value是出现的次数。
单词首次出现时,加入map中。每再次扫描到这个单词,hashmap对应的value加一。然后将value和堆顶(100个元素里最小的)作比较。如果新单词的value大于堆顶,然后将新元素插入堆。如果堆的容量大于100了,那就再删掉最小元素。确保堆的容量始终是100。
while (matcher.find()) { String s = matcher.group().toLowerCase(); if (s.endsWith("-")) { s = s.substring(0, s.length() - 1); } int count = map.getOrDefault(s, 0) + 1; map.put(s, count); if (queue.isEmpty()) { queue.add(s); } else { if (map.getOrDefault(s, 0) >= map.get(queue.peek())) { if (queue.contains(s)) { queue.remove(s); } queue.add(s); } if (queue.size() >= 100) { queue.poll(); } } }
优先队列因为需要使用特殊的比较策略(比较map中以单词为key对应的value),所以我们需重写比较器。当两者出现次数不同时,出现次数多的即为大者。二者次数相同时,比较字典序。
queue = new PriorityQueue<>(new Comparator<String>() { @Override public int compare(String o1, String o2) { int c1 = map.getOrDefault(o1, 0); int c2 = map.getOrDefault(o2, 0); if (c1 - c2 != 0) { return c1 - c2; } return o1.compareTo(o2); } });
3. 测试用例
见下图
Test Case ID 测试用例编号 | Test Item 测试项(即功能模块或函数) | Test Case Title 测试用例标题 | Test Criticality重要级别 | Status 是否通过 |
---|---|---|---|---|
1 | Main | BasicA | 5 | OK |
2 | Main | BasicB | 4 | OK |
3 | Main | Advanced | 4 | OK |
4 | Main | Special | 2 | OK |
5 | Counter | testm 1 | 3 | OK |
6 | Counter | testm 2 | 1 | OK |
7 | Counter | testm 3 | 5 | OK |
8 | Counter | testm 4 | 3 | OK |
9 | Counter | testm 5 | 4 | OK |
10 | Counter | testm 6 | 2 | OK |
11 | Counter | testm 7 | 5 | OK |
12 | Counter | testm 8 | 1 | OK |
13 | Counter | testm 9 | 3 | OK |
14 | Counter | testm 10 | 4 | OK |
15 | Counter | testm 11 | 5 | OK |
16 | Counter | testm 12 | 3 | OK |
17 | Counter | testm 13 | 4 | OK |
18 | Counter | testm 14 | 5 | OK |
19 | Counter | testm 15 | 3 | OK |
20 | Counter | testm 16 | 2 | OK |
21 | Counter | testm 17 | 3 | OK |
22 | Counter | testm 18 | 5 | OK |
23 | Counter | testm 19 | 5 | OK |
24 | Counter | testm 20 | 3 | OK |
25 | Counter | testl 1 | 2 | OK |
26 | Counter | testl 2 | 3 | OK |
27 | Counter | testl 3 | 4 | OK |
28 | Counter | testl 4 | 5 | OK |
29 | Counter | testl 5 | 5 | OK |
30 | Counter | testl 6 | 3 | OK |
31 | Counter | testl 7 | 3 | OK |
32 | Counter | testl 8 | 3 | OK |
33 | Counter | testl 9 | 4 | OK |
34 | Counter | testl 10 | 5 | OK |
35 | Counter | testl 11 | 5 | OK |
36 | Counter | testl 12 | 2 | OK |
37 | Counter | testl 13 | 2 | OK |
38 | Counter | testl 14 | 4 | OK |
39 | Counter | testl 15 | 1 | OK |
40 | Counter | testl 16 | 3 | OK |
41 | Counter | testl 17 | 1 | OK |
42 | Counter | testl 18 | 3 | OK |
43 | Counter | testl 19 | 4 | OK |
44 | Counter | testl 20 | 3 | OK |
45 | Counter | testz 1 | 5 | OK |
46 | Counter | testz 2 | 1 | OK |
47 | Counter | testz 3 | 2 | OK |
48 | Counter | testz 4 | 4 | OK |
49 | Counter | testz 5 | 5 | OK |
50 | Counter | testz 6 | 5 | OK |
51 | Counter | testz 7 | 2 | OK |
52 | Counter | testz 8 | 3 | OK |
53 | Counter | testz 9 | 4 | OK |
54 | Counter | testz 10 | 3 | OK |
55 | Counter | testz 11 | 4 | OK |
56 | Output | testz 12 | 3 | OK |
57 | Output | testz 13 | 5 | OK |
58 | Output | testz 14 | 4 | OK |
59 | Output | testz 15 | 3 | OK |
60 | Output | testz 16 | 2 | OK |
4. 单元测试结果
5. 评价小组成员代码
5.1 代码规范选择
使用《阿里巴巴 Java 开发手册》作为代码规范。
5.2 同行评审过程描述
作者,讲解员:朱一帆
主持人,评审员:李敏达
记录员,评审员:明煦智
评审目的
在已有的代码以及相应的代码规范基础上,我们进行代码风格以及代码质量的评审,目的有:首先是规范开发标准,确定开发的流程规范,确保开发进行过程中感到规范性。其次,遵循良好的代码规范可以让开发易于理解,便于团队合作,也利于后期维护。再者,通过评审代码,规范代码风格,提高代码质量时我们可以提高我们的代码水平,提升我们自己的工程能力,有利于我们成长为一个成熟健全的软件工程人。
经过讨论,两位评审员对于以下代码给出了评审结果是:
1. 此代码功能划分良好,按照相应的doc规范书写了比较完整的函数定义doc,但是缺少相应的行间注释,有可能造成代码的部分功能不明确。
2. 函数命名遵守了小驼峰命名,类命名遵守了大驼峰命名,语意清晰。
3. 代码冗余度小,代码风格明确,有良好的缩进习惯。
6. 静态测试
使用 Intellij IDEA 的 lint 插件,确保命名遵循驼峰法,无拼写错误,花括号换行等没有出现问题。
7. 性能优化
在第一个版本中,使用HashMap统计词频,然后将map里的每一项提到数组中排序,取最大的前100项作为结果。这样程序复杂度为O(nlogn)。
在目前展现在博客中的版本中,使用哈希表加最小堆的操作,假设堆中固定由k个元素,考虑最坏情况,每次都会调整堆序,程序复杂度的上界为O(nlogk)。考虑到,输入规模n可能会很大,但是在本题中,k固定为100,log2(100)可以视作一个常数。
事实上,经过本人对一些常见来源文章(例如新闻、书籍、发表文章)等的测试,Top 100的总是小部分几个常见词(The、this、do等),在大部分时间,我们是不需要调整堆序的。所以,理论上来讲平均情况应该是很接近于线性复杂度的。
在此版本中,将一份500MB左右的TXT的文件执行一遍,大约耗时100秒。
另外,有考虑过用多线程去优化统计速度,不过出现了一些同步问题,所以最终版本未表现出。
8. 小组贡献率
17055: 0.36
17066: 0.33
17057: 0.30