OO第三单元总结——略有枯燥的JML之旅
一、BUG分析&发现
本人在本单元作业中仅在第三次作业中存在BUG,分别是因为失误导致Delete Clod Emoji
删除不完整和最短路算法错误。而这两个BUG却修了很久,主要是这两个BUG在特定大数据下才会产生。对于问题定位会比较困难。主要是通过和同学互拍来进行BUG的确认与修正。
二、实现规格采取的设计策略
由于规格中有类之间的成员变量访问,但是接口中并没有预留成员变量的访问方法。为了保证自建类的封装性,我自己实现了一些方法来修改成员变量。如 add relation
My Network
public void addRelation(int id1, int id2, int value)
throws PersonIdNotFoundException, EqualRelationException {
if (getPerson(id1) == null) {
throw new MyPersonIdNotFoundException(id1);
}
if (getPerson(id2) == null) {
throw new MyPersonIdNotFoundException(id2);
}
Person p1 = getPerson(id1);
Person p2 = getPerson(id2);
mergeAndFind.union(id1, id2);
relationMatrix.addRe(id1, id2, id1 == id2 ? 0 : value);
//调用MyPerson类中的addRelation方法
((MyPerson) p1).addRelation(p2, value);
((MyPerson) p2).addRelation(p1, value);
for (Map.Entry<Integer, Group> entry : groupList.entrySet()) {
Group group = entry.getValue();
if (group.hasPerson(p1) && group.hasPerson(p2)) {
((MyGroup) group).setValueSum(group.getValueSum() + (2 * value));
}
}
}
My Person
public void addRelation(Person person, int value) throws EqualRelationException {
if (acquaintance.get(person) != null || this.equals(person)) {
throw new MyEqualRelationException(this.id, person.getId());
}
acquaintance.put(person, value);
}
在类似问题处理上,我尽量让被访问类提供相应功能,而不是将内部容器直接暴露给调用者,保证了一定的封装性。
三、容器选择&使用经验
我在本次作业中完全忽略了空间复杂度,而看重时间复杂度和类的封装性,所以对于键值类的数据大量使用了HashMap
有部分信息甚至发生了重复存储的情况。在容器选择上,对于一般的无序角标访问数据,我会采取ArrayList
来存储;而对于无序值查找数据会采取HashSet
以提升速度;对于需要排序的情况则会采用TreeSet
作为容器。除此之外再加上存在键值对时用Map
类容器存储便是我本次作业用的所有容器。其中,对于Hash
类容器,由于它是通过HashCode
计算哈希值然后在相应位置进行查找(冲突在8以下为链表,更大则为红黑树)所以在使用时容器里的元素类若是重写了equals
方法则也需重写 hashCode
方法以确保相同元素的哈希值相同。而Tree类的容器遇到compareTo
返回0(相同)的元素则会覆盖,也是需要注意的。最后,用HashMap
时尽量不要采用ContainValue
方法,这会使该容器丧失O(1)
时间复杂度的优势,而回到O(n)
遍历的方法。
将性能问题和图的维护放到一起叙述的原因是笔者认为这两者息息相关。而图算法也确实是计算中时间复杂度区别最大的一个(从O(n^3)
到O(n*log(n))
)。除算法外,笔者认为影响性能的两个最大的因素分别是采取增量更新|完全更新;有暂存区|无暂存区。前者会收到指令类型的影响,增量更新对于在线修改查找会有比较良好的适配,如 增加人|关系 -> 取得小组求和值 -> 增加人|关系 -> 取得小组求和值
的方法增量更新会显著高于完全更新的策略,而对于 增加人|关系 -> 增加人|关系 -> 取得小组求和值 -> 取得小组求和值
类的数据两组无明显差别。而有无暂存区则会级大的影响连序查找的性能这点是显而易见的。关于图的维护,我采取的构建单例的独立算法类来实现,如并查集类,关系矩阵类。并在 network
中每次 addPerson
和addRelation
时调用类中的方法来维护这个单例类。这样的设计方法尽管会消耗更多的空间,但是和Network
的耦合度极低,避免了过度耦合导致维护困难的情况。