第四周作业 wordcount 优化
GitHub 地址:
https://github.com/llag9810/Software-Quality-and-Testing-HW2
PSP2.1 表格
PSP2.1 | PSP阶段 | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 10 | 10 |
· Estimate | · 估计这个任务需要多少时间 | 10 | 10 |
Development | 开发 | 80 | 75 |
· Analysis | · 需求分析 (包括学习新技术) | 5 | 5 |
· Design Spec | · 生成设计文档 | 8 | 5 |
· Design Review | · 设计复审 (和同事审核设计文档) | 7 | 5 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 5 | 5 |
· Design | · 具体设计 | 10 | 8 |
· Coding | · 具体编码 | 10 | 12 |
· Code Review | · 代码复审 | 15 | 15 |
· Test | · 测试(自我测试,修改代码,提交修改) | 20 | 20 |
Reporting | 报告 | 40 | 55 |
· Test Report | · 测试报告 | 10 | 10 |
· Size Measurement | · 计算工作量 | 5 | 5 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 25 | 30 |
合计 | 130 | 140 |
程序实现
本程序分为三个部分:
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);
}
});
测试设计描述
测试设计从以下几个角度来表现:
- 空用例
- 单个单词用例
- 单句子单单词用例
- 多个同句子用例
- 多个不同句子用例
- 长文本不换行用例
- 长文本用例
- 超大文件用例
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 |
单元测试结果
评价小组成员代码
代码规范选择
使用《阿里巴巴 Java 开发手册》作为代码规范。
同行评审过程描述
作者,讲解员:朱一帆
主持人,评审员:李敏达
记录员,评审员:明煦智
评审目的
在已有的代码以及相应的代码规范基础上,我们进行代码风格以及代码质量的评审,目的有:首先是规范开发标准,确定开发的流程规范,确保开发进行过程中感到规范性。其次,遵循良好的代码规范可以让开发易于理解,便于团队合作,也利于后期维护。再者,通过评审代码,规范代码风格,提高代码质量时我们可以提高我们的代码水平,提升我们自己的工程能力,有利于我们成长为一个成熟健全的软件工程人。
经过讨论,两位评审员对于以下代码给出了评审结果是:
1. 此代码功能划分良好,按照相应的doc规范书写了比较完整的函数定义doc,但是缺少相应的行间注释,有可能造成代码的部分功能不明确。
2. 函数命名遵守了小驼峰命名,类命名遵守了大驼峰命名,语意清晰。
3. 代码冗余度小,代码风格明确,有良好的缩进习惯。
静态测试
使用 Intellij IDEA 的 lint 插件,确保命名遵循驼峰法,无拼写错误,花括号换行等没有出现问题。
性能优化
在第一个版本中,使用HashMap统计词频,然后将map里的每一项提到数组中排序,取最大的前100项作为结果。这样程序复杂度为O(nlogn)。
在目前展现在博客中的版本中,使用哈希表加最小堆的操作,假设堆中固定由k个元素,考虑最坏情况,每次都会调整堆序,程序复杂度的上界为O(nlogk)。考虑到,输入规模n可能会很大,但是在本题中,k固定为100,log2(100)可以视作一个常数。
事实上,经过本人对一些常见来源文章(例如新闻、书籍、发表文章)等的测试,Top 100的总是小部分几个常见词(The、this、do等),在大部分时间,我们是不需要调整堆序的。所以,理论上来讲平均情况应该是很接近于线性复杂度的。
另外,有考虑过用多线程去优化统计速度,不过出现了一些同步问题,所以最终版本未表现出。
小组贡献率
17055: 0.36
17066: 0.33
17057: 0.30