OOunit3总结(模拟社交网络)
本单元的作业是根据JML规格,实现社交关系模拟系统,难度相比第二单元下降了很多,但是我的得分情况却差了很多,主要原因是本地没有充分测试。
一、实现规格所采取的设计策略
本单元的代码只需要按照JML的规格实现方法就行,我在刚开始写的时候,按照指导书中每个类出现的顺序来填充他们的方法,而没有从整体上观察我们需要实现什么功能,只是盲目地翻译了JML。虽然这样只要每个方法的实现正确,最后就能保证结果的正确性,但是性能上非常糟糕。例如没有选择合适的容器,也没有引入任何中间变量来降低时间复杂度。
在完成后两次作业时,我选择了先整体浏览一遍,按功能确定完成顺序,例如Network留至最后完成,先实现异常处理再完成正常情况处理等。
二、测试方法和策略
本单元可以采取对拍的方法测试,生成测试数据对比不同人的输出。但是由于时间问题,还没有学会写测试工具的我直接看代码从逻辑上验证正确性。
其实本单元的正确性相对容易保证,但是我的后两次作业强测均出现了正确性问题。
第二次作业是因为算方差时没有除以总人数(看JML看的眼花了),仅得10分,其实在阅读规格时仔细一点就不会出这种问题。
第三次作业是发红包消息时没有减Person1的money。(看JML的时候看串行了)
这单元的我出现的bug基本都属于直接肉眼看代码就能看出来的,但是因为自己没有测试,所以犯了很多致命的导致结果全错的bug(好处大概是很好修?基本3行内就修完了)。
三、容器的选择和使用
ArrayList:刚开始使用了ArrayList,因为这是我使用最熟练的,从pre用到现在,而且一开始完全没考虑性能,只要实现了就行,所以都无脑使用了ArrayList。虽然知道有HashMap等其他容器,但是想着以简单为由用了自己熟悉的。(事实证明偷懒是没用的)HashMap:第一次作业写了一大半终于意识到全部用ArrayList极大影响性能,弃之转向HashMap。因为id都是唯一的,而且除了Person中的Messages,其他都没有顺序要求,所以使用HashMap十分方便查找。LinkedList:Person类中的Messages因为有顺序要求,所以考虑用LinkedList和ArrayList,在读了其他函数的规格后,发现需要在Messages前插入Message。查了这两种容器的区别后,LinkedList以链表实现。以于是选择了更为方便在头部插入元素的LinkedList。
四、性能问题
第一次作业刚开始时,我全部采用了ArrayList,很多方法的实现也是遍历和循环,效率非常低下。ArrayList换乘HashMap后根据id查找Person或Message的复杂度变为O(1)。
isCircle方法判断是否连通时学习了并查集的方法,每次添加关系时更新根节点,并进行路径压缩,相比之前用深搜时间复杂度降低了。
queryNameRank方法查询当前名字排序位置起初是遍历计算排序,后来改为字典树统计,每个结点记录以当前结点为终点的个数、经过该结点的次数和所有子结点信息。
平均年龄和方差的计算,第二次作业强测前,每次求平均值时我都要遍历一遍,导致CTLE了,修复bug时增加中间变量ageSum存储年龄总和,在加人或删人时更新。求方差时本打算引入中间变量记录平方和,然后利用\(E(x^2)-E(x)^2\)计算方差,但是由于int相除时取整损失精度的问题,这样算出来结果和答案不一样。最后我还是没能找到取整后一样的算法,回到了遍历求方差。
最短路径算法采用了Dijkstra算法,又学习了堆优化后的Dijkstra,不过强测还是CTLE了。试图增加一些中间变量优化算法,但是没有维护好正确性反而缺失了,暂时还停留在CTLE的版本。
五、结构设计
本单元实现的社交网络系统中,以Person为结点,以是否认识的关系构建边,认识则有边,边赋予权重value,连通的两个结点可以发送消息,Person也可以给一个包含多个Person的group发送消息。
-
MyNetwork, 存储people,groups,messages,emojis这些规格中已有的要求存储的信息,另外为判断是否连通增加connection记录根结点。MyNetWork实现了对人员、群组、消息的管理。 -
MyPerson,实现查询和修改人员信息。 -
MyMessage,实现对消息属性的查询。 -
MyGroup,实现群内人员增减和总体情况查询。 -
connection,为记录根结点而增加的类。 -
Node,为实现字典树结构而增加的类,方便查询nameRank。 -
Dijkstra,GraphNode为实现堆优化的Dijkstra算法而增加的类,GraphNode为堆结点。

浙公网安备 33010602011771号