第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

 

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