第四周作业 wordcount 优化

GitHub 地址:

https://github.com/llag9810/Software-Quality-and-Testing-HW2

 

PSP2.1 表格

PSP2.1PSP阶段预估耗时(分钟)实际耗时(分钟)
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

posted @ 2018-04-08 20:34  immm  阅读(163)  评论(0编辑  收藏  举报