JML理论基础与工具链
JML理论基础
JML是一种将java程序规格化表达的一种语言,其描述的是行为的规格,而非具体的代码实现。
通过对程序开展规格化设计,当然前提是使用JML这种规范化的语言,可以使得代码具有严谨的逻辑,因为代码的实现一定按照规格去实现,而规格是逻辑严谨的。
同时代码的可维护性进一步提高,书写规格保证了可以在不写代码的情况下,首先描述其功能,等到需要写的时候再进行代码书写,提高可维护性。
工具链
一系列JML相关的支持工具为开发者提供了便捷的验证与构造方法,为JML的传播起到了积极作用。下面我总结了一些实用的,建议在JML规格语言之上的工具。
-
OpenJml
一个可以自动检测JML规范的插件
-
JMLdoc与JMLunit
前者与javadoc类似,后者可自动生成测试
SMT Solver部署与部分方法验证
下载OpenJML的jar文件,并进行测试。
由于并未部署环境,采用命令java -jar <path>/openjml.jar -esc <soursefile>
进行验证与处理,我用第三次作业的MyPath.java。
首先进行语法检测
1 chenrenjiedeMacBook-Pro:openjml-0.8.42-20190401 chenrenjie$ java -jar openjml.jar -cp /Users/chenrenjie/Desktop/TEST/specs-homework-3-1.3-raw-jar-with-dependencies.jar -check /Users/chenrenjie/Desktop/TEST/src/MyPath.java 2 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:63: 错误: 找不到符号 3 @ ensures \result == (((Path) obj).nodes.length == nodes.length) && 4 ^ 5 符号: 变量 nodes 6 位置: 接口 Path 7 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:64: 错误: 找不到符号 8 @ (\forall int i; 0 <= i && i < nodes.length; nodes[i] == ((Path) obj).nodes[i]); 9 ^ 10 符号: 变量 nodes 11 位置: 接口 Path 12 2 个错误
将Path改成MyPath后,再检查一次
1 chenrenjiedeMacBook-Pro:openjml-0.8.42-20190401 chenrenjie$ java -jar openjml.jar -cp /Users/chenrenjie/Desktop/TEST/specs-homework-3-1.3-raw-jar-with-dependencies.jar -check /Users/chenrenjie/Desktop/TEST/src/MyPath.java 2 chenrenjiedeMacBook-Pro:openjml-0.8.42-20190401 chenrenjie$
可通过语法检测
再进行代码静态检查
1 chenrenjiedeMacBook-Pro:openjml-0.8.42-20190401 chenrenjie$ java -jar openjml.jar -cp /Users/chenrenjie/Desktop/TEST/specs-homework-3-1.3-raw-jar-with-dependencies.jar -esc /Users/chenrenjie/Desktop/TEST/src/MyPath.java 2 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:18: 警告: The prover cannot establish an assertion (PossiblyNegativeIndex) in method MyPath 3 arrayList.add(nodeList[i]); 4 ^ 5 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:13: 警告: The prover cannot establish an assertion (NullField) in method MyPath 6 //@ public instance model non_null int[] nodes; 7 ^ 8 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:113: 警告: The prover cannot establish an assertion (PossiblyBadCast) in method compareTo: a java.lang.Object cannot be proved to be a java.lang.Integer 9 Integer integer1 = (Integer) iterator1.next(); 10 ^ 11 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:114: 警告: The prover cannot establish an assertion (PossiblyBadCast) in method compareTo: a java.lang.Object cannot be proved to be a java.lang.Integer 12 Integer integer2 = (Integer) iterator2.next(); 13 ^ 14 openjml.jar(specs/java/util/Collection.jml):70: 警告: The prover cannot establish an assertion (Invariant) in method compareTo 15 //-RAC@ public invariant content.owner == this; 16 ^ 17 openjml.jar(specs/java/util/Collection.jml):71: 警告: The prover cannot establish an assertion (Invariant) in method compareTo 18 //-RAC@ public invariant content.theSize >= 0; 19 ^ 20 openjml.jar(specs/java/util/Map.jml):76: 警告: The prover cannot establish an assertion (Invariant) in method compareTo 21 public invariant content.owner == this; 22 ^ 23 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:46: 警告: The prover cannot establish an assertion (Postcondition: /Users/chenrenjie/Desktop/TEST/src/MyPath.java:44: 注: ) in method containsNode 24 return this.node.containsKey(node); 25 ^ 26 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:44: 警告: Associated declaration: /Users/chenrenjie/Desktop/TEST/src/MyPath.java:46: 注: 27 //@ ensures \result == (\exists int i; 0 <= i && i < nodes.length; nodes[i] == node); 28 ^ 29 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:73: 警告: The prover cannot establish an assertion (Assignable: /Users/chenrenjie/Desktop/TEST/src/MyPath.java:60: 注: ) in method equals: \everything 30 if (((Path) obj).size() == this.size()) { 31 ^ 32 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:60: 警告: Associated declaration: /Users/chenrenjie/Desktop/TEST/src/MyPath.java:73: 注: 33 @ public normal_behavior 34 ^ 35 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:74: 警告: The prover cannot establish an assertion (Assignable: /Users/chenrenjie/Desktop/TEST/src/MyPath.java:60: 注: ) in method equals: \everything 36 Iterator iterator = ((Path) obj).iterator(); 37 ^ 38 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:60: 警告: Associated declaration: /Users/chenrenjie/Desktop/TEST/src/MyPath.java:74: 注: 39 @ public normal_behavior 40 ^ 41 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:76: 警告: The prover cannot establish an assertion (Assignable: /Users/chenrenjie/Desktop/TEST/src/MyPath.java:60: 注: ) in method equals: moreElements 42 Integer integer1 = (Integer) iterator.next(); 43 ^ 44 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:60: 警告: Associated declaration: /Users/chenrenjie/Desktop/TEST/src/MyPath.java:76: 注: 45 @ public normal_behavior 46 ^ 47 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:76: 警告: The prover cannot establish an assertion (Assignable: /Users/chenrenjie/Desktop/TEST/src/MyPath.java:60: 注: ) in method equals: remove_called_since 48 Integer integer1 = (Integer) iterator.next(); 49 ^ 50 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:60: 警告: Associated declaration: /Users/chenrenjie/Desktop/TEST/src/MyPath.java:76: 注: 51 @ public normal_behavior 52 ^ 53 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:76: 警告: The prover cannot establish an assertion (Assignable: /Users/chenrenjie/Desktop/TEST/src/MyPath.java:60: 注: ) in method equals: objectState 54 Integer integer1 = (Integer) iterator.next(); 55 ^ 56 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:60: 警告: Associated declaration: /Users/chenrenjie/Desktop/TEST/src/MyPath.java:76: 注: 57 @ public normal_behavior 58 ^ 59 openjml.jar(specs/java/util/Collection.jml):70: 警告: The prover cannot establish an assertion (Invariant) in method equals 60 //-RAC@ public invariant content.owner == this; 61 ^ 62 openjml.jar(specs/java/util/Collection.jml):71: 警告: The prover cannot establish an assertion (Invariant) in method equals 63 //-RAC@ public invariant content.theSize >= 0; 64 ^ 65 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:81: 警告: The prover cannot establish an assertion (Postcondition: /Users/chenrenjie/Desktop/TEST/src/MyPath.java:63: 注: ) in method equals 66 return true; 67 ^ 68 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:63: 警告: Associated declaration: /Users/chenrenjie/Desktop/TEST/src/MyPath.java:81: 注: 69 @ ensures \result == (((MyPath) obj).nodes.length == nodes.length) && 70 ^ 71 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:63: 警告: The prover cannot establish an assertion (UndefinedBadCast) in method equals: a java.lang.Object cannot be proved to be a MyPath 72 @ ensures \result == (((MyPath) obj).nodes.length == nodes.length) && 73 ^ 74 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:81: 警告: Associated method exit 75 return true; 76 ^ 77 openjml.jar(specs/java/util/Map.jml):76: 警告: The prover cannot establish an assertion (Invariant) in method equals 78 public invariant content.owner == this; 79 ^ 80 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:76: 警告: The prover cannot establish an assertion (PossiblyBadCast) in method equals: a java.lang.Object cannot be proved to be a java.lang.Integer 81 Integer integer1 = (Integer) iterator.next(); 82 ^ 83 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:78: 警告: The prover cannot establish an assertion (Postcondition: openjml.jar(specs/java/lang/Object.jml):78: 注: ) in method equals 84 return false; 85 ^ 86 openjml.jar(specs/java/lang/Object.jml):78: 警告: Associated declaration: /Users/chenrenjie/Desktop/TEST/src/MyPath.java:78: 注: 87 @ ensures \result; 88 ^ 89 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:76: 警告: The prover cannot establish an assertion (ExceptionalPostcondition: /Users/chenrenjie/Desktop/TEST/src/MyPath.java:60: 注: ) in method equals 90 Integer integer1 = (Integer) iterator.next(); 91 ^ 92 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:60: 警告: Associated declaration: /Users/chenrenjie/Desktop/TEST/src/MyPath.java:76: 注: 93 @ public normal_behavior 94 ^ 95 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:76: 警告: The prover cannot establish an assertion (ExceptionalPostcondition: openjml.jar(specs/java/lang/Object.jml):76: 注: ) in method equals 96 Integer integer1 = (Integer) iterator.next(); 97 ^ 98 openjml.jar(specs/java/lang/Object.jml):76: 警告: Associated declaration: /Users/chenrenjie/Desktop/TEST/src/MyPath.java:76: 注: 99 @ public normal_behavior 100 ^ 101 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:56: 警告: The prover cannot establish an assertion (Postcondition: /Users/chenrenjie/Desktop/TEST/src/MyPath.java:50: 注: ) in method getDistinctNodeCount 102 return node.size(); 103 ^ 104 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:50: 警告: Associated declaration: /Users/chenrenjie/Desktop/TEST/src/MyPath.java:56: 注: 105 @ ensures (\exists int[] arr; (\forall int i, j; 0 <= i && i < j && j < arr.length; arr[i] != arr[j]); 106 ^ 107 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:40: 警告: The prover cannot establish an assertion (Postcondition: /Users/chenrenjie/Desktop/TEST/src/MyPath.java:37: 注: ) in method getNode 108 return arrayList.get(index); 109 ^ 110 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:37: 警告: Associated declaration: /Users/chenrenjie/Desktop/TEST/src/MyPath.java:40: 注: 111 @ ensures \result == nodes[index]; 112 ^ 113 错误: ESC could not be attempted because of a failure in typechecking or AST transformation: getUnpleasantValue 114 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:138: 警告: The prover cannot establish an assertion (Postcondition: openjml.jar(specs/java/lang/Object.jml):63: 注: ) in method hashCode 115 return Arrays.hashCode(arrayList.toArray()); 116 ^ 117 openjml.jar(specs/java/lang/Object.jml):63: 警告: Associated declaration: /Users/chenrenjie/Desktop/TEST/src/MyPath.java:138: 注: 118 //-RAC@ ensures \result == theHashCode; 119 ^ 120 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:31: 警告: The prover cannot establish an assertion (Postcondition: /Users/chenrenjie/Desktop/TEST/src/MyPath.java:28: 注: ) in method size 121 return arrayList.size(); 122 ^ 123 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:28: 警告: Associated declaration: /Users/chenrenjie/Desktop/TEST/src/MyPath.java:31: 注: 124 @ ensures \result == nodes.length; 125 ^ 126 41 个警告
发现了很多警告,选择其中三个进行分析:
-
分析样例一
1 //@ public instance model non_null int[] nodes; 2 public MyPath(int[] nodeList) { 3 arrayList = new ArrayList<>(); 4 node = new HashMap<>(); 5 for (int i = 0; i < nodeList.length; i++) { 6 arrayList.add(nodeList[i]); 7 if (node.containsKey(nodeList[i])) { 8 node.put(nodeList[i], node.get(nodeList[i]) + 1); 9 } else { 10 node.put(nodeList[i], 1); 11 } 12 } 13 }
1 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:18: 警告: The prover cannot establish an assertion (PossiblyNegativeIndex) in method MyPath 2 arrayList.add(nodeList[i]); 3 ^
1 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:76: 警告: The prover cannot establish an assertion (ExceptionalPostcondition: /Users/chenrenjie/Desktop/TEST/src/MyPath.java:60: 注: ) in method equals 2 Integer integer1 = (Integer) iterator.next(); 3 ^
1 public boolean equals(Object obj) { 2 if (obj instanceof Path) { 3 if (((Path) obj).size() == this.size()) { 4 Iterator iterator = ((Path) obj).iterator(); 5 for (Integer integer : this) { 6 Integer integer1 = (Integer) iterator.next(); 7 if (!Objects.equals(integer, integer1)) { 8 return false; 9 } 10 } 11 return true; 12 } 13 return false; 14 } else { 15 return false; 16 } 17 }
这里用到了强制类型转换,将一个Obj类强制转换成Integer,而这样的转换是有风险的,故产生了警告。
- 分析样例三
1 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:63: 警告: The prover cannot establish an assertion (UndefinedBadCast) in method equals: a java.lang.Object cannot be proved to be a MyPath 2 @ ensures \result == (((MyPath) obj).nodes.length == nodes.length) && 3 ^
这是规格中的转换问题,这种转换是不好的声明。
通过上述三个例子的分析,可以看到静态检查是在充分考虑了各种情况后进行的检查,其具有普适应,而对于独特的程序,有时候这种检查过于繁琐,往往会出现一些本不可能产生的情况,仍然被报警告。
JMLUnitNG部署与测试用例生成
针对本次作业的Graph和Path,分别进行了测试,结果都是失败的。原因可能是在我的程序中无法对单独的java文件进行测试,其文件相互有所依赖。致使如下结果
1 chenrenjiedeMacBook-Pro:Desktop chenrenjie$ java -jar jmlunitng.jar -cp /Users/chenrenjie/Desktop/TEST3/specs-homework-2-1.2-raw-jar-with-dependencies.jar -sp /Users/chenrenjie/Desktop/specs-homework-3-opensource/src/main/java -d test /Users/chenrenjie/Desktop/TEST/src/MyPath.java 2 JMLUnitNG exited because of an irrecoverable error: 3 org.jmlspecs.jmlunitng.JMLUnitNGError: Encountered 16 compilation errors: 4 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:15: 错误: 非法的类型开始 5 arrayList = new ArrayList<>(); 6 ^ 7 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:16: 错误: 非法的类型开始 8 node = new HashMap<>(); 9 ^ 10 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:1: 错误: 程序包com.oocourse.specs3.models不存在 11 import com.oocourse.specs3.models.Path; 12 ^ 13 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:1: 错误: 程序包com.oocourse.specs3.models不存在 14 import com.oocourse.specs3.models.Path; 15 ^ 16 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/ArrayList.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 17 建议升级此编译器。 18 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/AbstractList.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 19 建议升级此编译器。 20 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/AbstractCollection.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 21 建议升级此编译器。 22 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/Object.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 23 建议升级此编译器。 24 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/Collection.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 25 建议升级此编译器。 26 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/Iterable.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 27 建议升级此编译器。 28 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/List.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 29 建议升级此编译器。 30 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/AbstractList$ListItr.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 31 建议升级此编译器。 32 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/AbstractList$Itr.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 33 建议升级此编译器。 34 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/RandomAccess.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 35 建议升级此编译器。 36 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/Cloneable.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 37 建议升级此编译器。 38 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/io/Serializable.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 39 建议升级此编译器。 40 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/ArrayList$ArrayListSpliterator.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 41 建议升级此编译器。 42 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/ArrayList$SubList.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 43 建议升级此编译器。 44 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/ArrayList$ListItr.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 45 建议升级此编译器。 46 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/ArrayList$Itr.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 47 建议升级此编译器。 48 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/HashMap.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 49 建议升级此编译器。 50 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/AbstractMap.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 51 建议升级此编译器。 52 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/Map.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 53 建议升级此编译器。 54 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/Map$Entry.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 55 建议升级此编译器。 56 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/AbstractMap$SimpleImmutableEntry.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 57 建议升级此编译器。 58 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/AbstractMap$SimpleEntry.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 59 建议升级此编译器。 60 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/HashMap$TreeNode.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 61 建议升级此编译器。 62 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/HashMap$EntrySpliterator.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 63 建议升级此编译器。 64 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/HashMap$ValueSpliterator.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 65 建议升级此编译器。 66 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/HashMap$KeySpliterator.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 67 建议升级此编译器。 68 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/HashMap$HashMapSpliterator.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 69 建议升级此编译器。 70 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/HashMap$EntryIterator.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 71 建议升级此编译器。 72 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/HashMap$ValueIterator.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 73 建议升级此编译器。 74 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/HashMap$KeyIterator.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 75 建议升级此编译器。 76 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/HashMap$HashIterator.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 77 建议升级此编译器。 78 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/HashMap$EntrySet.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 79 建议升级此编译器。 80 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/HashMap$Values.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 81 建议升级此编译器。 82 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/HashMap$KeySet.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 83 建议升级此编译器。 84 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/HashMap$Node.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 85 建议升级此编译器。 86 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/Iterator.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 87 建议升级此编译器。 88 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/Arrays.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 89 建议升级此编译器。 90 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/Arrays$ArrayList.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 91 建议升级此编译器。 92 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/Arrays$LegacyMergeSort.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 93 建议升级此编译器。 94 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/Arrays$NaturalOrder.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 95 建议升级此编译器。 96 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/Objects.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 97 建议升级此编译器。 98 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:9: 错误: 找不到符号 99 public class MyPath implements Path { 100 ^ 101 符号: 类 Path 102 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/Integer.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 103 建议升级此编译器。 104 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/Number.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 105 建议升级此编译器。 106 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/Comparable.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 107 建议升级此编译器。 108 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/Integer$IntegerCache.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 109 建议升级此编译器。 110 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:109: 错误: 找不到符号 111 public int compareTo(Path o) { 112 ^ 113 符号: 类 Path 114 位置: 类 MyPath 115 /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/ArrayList.class): 警告: 无法找到类型 'Profile+Annotation' 的注释方法 'value()': 找不到jdk.Profile+Annotation的类文件 116 /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/AbstractList.class): 警告: 无法找到类型 'Profile+Annotation' 的注释方法 'value()' 117 /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/AbstractCollection.class): 警告: 无法找到类型 'Profile+Annotation' 的注释方法 'value()' 118 /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/Object.class): 警告: 无法找到类型 'Profile+Annotation' 的注释方法 'value()' 119 /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/Collection.class): 警告: 无法找到类型 'Profile+Annotation' 的注释方法 'value()' 120 /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/Iterable.class): 警告: 无法找到类型 'Profile+Annotation' 的注释方法 'value()' 121 /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/List.class): 警告: 无法找到类型 'Profile+Annotation' 的注释方法 'value()' 122 /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/RandomAccess.class): 警告: 无法找到类型 'Profile+Annotation' 的注释方法 'value()' 123 /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/Cloneable.class): 警告: 无法找到类型 'Profile+Annotation' 的注释方法 'value()' 124 /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/io/Serializable.class): 警告: 无法找到类型 'Profile+Annotation' 的注释方法 'value()' 125 /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/HashMap.class): 警告: 无法找到类型 'Profile+Annotation' 的注释方法 'value()' 126 /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/AbstractMap.class): 警告: 无法找到类型 'Profile+Annotation' 的注释方法 'value()' 127 /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/Map.class): 警告: 无法找到类型 'Profile+Annotation' 的注释方法 'value()' 128 /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/Iterator.class): 警告: 无法找到类型 'Profile+Annotation' 的注释方法 'value()' 129 /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/Arrays.class): 警告: 无法找到类型 'Profile+Annotation' 的注释方法 'value()' 130 /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/util/Objects.class): 警告: 无法找到类型 'Profile+Annotation' 的注释方法 'value()' 131 /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/Integer.class): 警告: 无法找到类型 'Profile+Annotation' 的注释方法 'value()' 132 /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/Number.class): 警告: 无法找到类型 'Profile+Annotation' 的注释方法 'value()' 133 /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/Comparable.class): 警告: 无法找到类型 'Profile+Annotation' 的注释方法 'value()' 134 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/annotation/Annotation.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 135 建议升级此编译器。 136 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/Override.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 137 建议升级此编译器。 138 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/annotation/Retention.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 139 建议升级此编译器。 140 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/annotation/RetentionPolicy.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 141 建议升级此编译器。 142 /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/annotation/Annotation.class): 警告: 无法找到类型 'Profile+Annotation' 的注释方法 'value()' 143 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/annotation/Target.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 144 建议升级此编译器。 145 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/annotation/ElementType.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 146 建议升级此编译器。 147 /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/Override.class): 警告: 无法找到类型 'Profile+Annotation' 的注释方法 'value()' 148 /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/annotation/Retention.class): 警告: 无法找到类型 'Profile+Annotation' 的注释方法 'value()' 149 /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/annotation/RetentionPolicy.class): 警告: 无法找到类型 'Profile+Annotation' 的注释方法 'value()' 150 /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/annotation/Target.class): 警告: 无法找到类型 'Profile+Annotation' 的注释方法 'value()' 151 /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/annotation/ElementType.class): 警告: 无法找到类型 'Profile+Annotation' 的注释方法 'value()' 152 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/AutoCloseable.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 153 建议升级此编译器。 154 /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/AutoCloseable.class): 警告: 无法找到类型 'Profile+Annotation' 的注释方法 'value()' 155 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:16: 错误: 类型变量数目错误; 需要2 156 node = new HashMap<>(); 157 ^ 158 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/String.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 159 建议升级此编译器。 160 /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/String.class): 警告: 无法找到类型 'Profile+Annotation' 的注释方法 'value()' 161 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/Byte.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 162 建议升级此编译器。 163 /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/Byte.class): 警告: 无法找到类型 'Profile+Annotation' 的注释方法 'value()' 164 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/Character.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 165 建议升级此编译器。 166 /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/Character.class): 警告: 无法找到类型 'Profile+Annotation' 的注释方法 'value()' 167 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/Short.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 168 建议升级此编译器。 169 /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/Short.class): 警告: 无法找到类型 'Profile+Annotation' 的注释方法 'value()' 170 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/RuntimeException.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 171 建议升级此编译器。 172 /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/RuntimeException.class): 警告: 无法找到类型 'Profile+Annotation' 的注释方法 'value()' 173 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:72: 错误: 找不到符号 174 if (obj instanceof Path) { 175 ^ 176 符号: 类 Path 177 位置: 类 MyPath 178 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:73: 错误: 找不到符号 179 if (((Path) obj).size() == this.size()) { 180 ^ 181 符号: 类 Path 182 位置: 类 MyPath 183 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:74: 错误: 找不到符号 184 Iterator iterator = ((Path) obj).iterator(); 185 ^ 186 符号: 类 Path 187 位置: 类 MyPath 188 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:75: 错误: for-each 不适用于表达式类型 189 for (Integer integer : this) { 190 ^ 191 要求: {1} 192 找到: {0} 193 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:61: 错误: 找不到符号 194 @ requires obj != null && obj instanceof Path; 195 ^ 196 符号: 类 Path 197 位置: 类 MyPath 198 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:67: 错误: 找不到符号 199 @ requires obj == null || !(obj instanceof Path); 200 ^ 201 符号: 类 Path 202 位置: 类 MyPath 203 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/Math.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 204 建议升级此编译器。 205 /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/Math.class): 警告: 无法找到类型 'Profile+Annotation' 的注释方法 'value()' 206 警告: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/Math$RandomNumberGeneratorHolder.class): 主版本 52 比 51 新, 此编译器支持最新的主版本。 207 建议升级此编译器。 208 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:108: 错误: 方法不会覆盖或实现超类型的方法 209 @Override 210 ^ 211 /Users/chenrenjie/Desktop/TEST/src/MyPath.java:131: 错误: 方法不会覆盖或实现超类型的方法 212 @Override 213 ^ 214 /Users/chenrenjie/Desktop/specs-homework-3-opensource/src/main/java/com/oocourse/specs3/models/Path.java:3: 错误: 找不到符号 215 public interface Path extends Iterable<Integer>, Comparable<Path> { 216 ^ 217 符号: 类 Path 218 219 at org.jmlspecs.jmlunitng.JMLUnitNG.processAllCompilationUnits(JMLUnitNG.java:527) 220 at org.jmlspecs.jmlunitng.JMLUnitNG.run(JMLUnitNG.java:414) 221 at org.jmlspecs.jmlunitng.JMLUnitNG.main(JMLUnitNG.java:177)
那么换一个java文件进行尝试。
1 public class compare { 2 /*@ public normal_behaviour 3 @ ensures \result == lhs - rhs; 4 */ 5 public static int compare(int lhs, int rhs) { 6 return lhs - rhs; 7 } 8 9 public static void main(String[] args) { 10 compare(114514, 1919810); 11 } 12 }
模拟这样的测试,来源于讨论区,运行命令
1 chenrenjiedeMacBook-Pro:Desktop chenrenjie$ java -jar /Users/chenrenjie/Desktop/jmlunitng.jar /Users/chenrenjie/Desktop/demo/src/compare.java
可以得到如下结果
再进行如下命令
1 chenrenjiedeMacBook-Pro:Desktop chenrenjie$ javac -cp /Users/chenrenjie/Desktop/jmlunitng.jar /Users/chenrenjie/Desktop/demo/src/*.java
可成功编译,再执行openjml的指令
1 chenrenjiedeMacBook-Pro:openjml-0.8.42-20190401 chenrenjie$ java -jar openjml.jar -rac /Users/chenrenjie/Desktop/demo/src/compare.java
最后运行
1 chenrenjiedeMacBook-Pro:Desktop chenrenjie$ java -classpath /Users/chenrenjie/Desktop/jmlunitng.jar:/Users/chenrenjie/Desktop/demo/src compare_JML_Test
得到结果如下
1 [TestNG] Running: 2 Command line suite 3 4 Passed: racEnabled() 5 Passed: constructor compare() 6 Passed: constructor compare(-2147483648, -2147483648) 7 Failed: constructor compare(0, -2147483648) 8 Failed: constructor compare(2147483647, -2147483648) 9 Passed: constructor compare(-2147483648, 0) 10 Passed: constructor compare(0, 0) 11 Passed: constructor compare(2147483647, 0) 12 Failed: constructor compare(-2147483648, 2147483647) 13 Passed: constructor compare(0, 2147483647) 14 Passed: constructor compare(2147483647, 2147483647) 15 Passed: static main(null) 16 Passed: static main({}) 17 18 =============================================== 19 Command line suite 20 Total tests run: 13, Failures: 3, Skips: 0 21 ===============================================
可以看到,其主要是用了一些边界数据进行比较,这样有助于极端情况下的测试。
但对于一些复杂的java文件,这种样例的构造往往很困难。
架构设计梳理
三次作业仍然是增量式的设计,在每次设计上增加新的功能,大范围的保持原架构的不变。
-
按照指导书的要求完成两个类,分别实现各种方法即可。需要注意的是,为了更好的控制复杂度,最好采用相关复杂度低的容器,比如用hashmap完成查找这一功能。
对于MyPath内部,只需要构建一个arraylist去顺序存储其nodeid,从而使得index的索引复杂度为O(1),而后构建一个
key = nodeid, value = count
的hashmap,可以很方便的统计不同节点的个数。对于MyPathContainer,需要完成path与index的双向映射,还需要一个hashmap去储存所有的node与引用次数,由此可以很快速的统计出不同的节点。
对于增删操作,只需要对不同的节点的次数进行减1,并将双向映射相应修改即可。
-
在第一次作业的基础上增加一些新的数据结构,为了使得代码修改量小,便直接在原来的基础上增加代码,而不进行重构的修改。
首先从一个单一的容器的结构,变成一个图结构,并且还需要对图中的邻接情况,最短路径等进行考虑,因此需要引入邻接矩阵,其表现形式为
HashMap<nodeid, HashMap<nodeid, count>>
,从而每个节点都有一个hashmap去储存着邻接的节点与引用次数,相应的增删也需要对矩阵进行修改。而后引入同样的结构的最短距离矩阵,以作为一个缓存,显然每当增删的时候,需要对其进行清空。关于isconnect方法的处理,由于此次作业的边权值为1,可以直接采用BFS的方法处理,这样处理速度快,效率高,可以中间缓存大量的节点而非一个节点的最短距离。
此处的不足之处在于并未进行双向缓存,以及一次BFS仅仅截止到了终点,事实上是可以全部遍历完全的,有时候这样效率可能会更高一些。
其他的内容均与第一次作业保持一直,这样的设计增加了复杂度,有些东西本可以合并在一起,考虑到代码效率和正确性的风险,采用原来的功能用原来的代码,新功能用新代码,两者互不相关,可以最大程度确保新加的代码不会影响上次指令的正确性,缺点也很明显,冗余,复杂。
-
第三次作业对架构能力考验更大,对全局设计的能力有着更强的要求。不幸的是,我仍然采用了上述的设计模式,不改源代码,在基础上不断增加,致使代码超过1000行...
第三次作业主要是研究四个权值不一样的最短路径的问题,当然还有一个连通块问题。
其中四个最短路径可以采用统一的一个dijkstra进行模拟,事实上我只在三个最短路径采用dijkstra,最简单的最短路径仍采用bfs,原因仍是不改代码。那么由于增加了换乘需求,因此考虑何处换乘,成为了一个困扰自己的最重要的问题。此站是否需要换乘?在困扰一天之后,我开始动笔进行书写,我考虑的是尽可能不换乘,从而用dijkstra不断扩展,最后我发现,它本质上并不是局部最优就是全局最优,尤其是在一个节点在多条线的时候,很难描述清楚这种杂糅的状态,因此不得不,也一定要进行拆点的处理。
与讨论区的大神不同的是,我的拆点是将一个点,如果被多个path所引用,则其拆分成多个Node,每个Node有一个pathid和nodeid,这样一个成为多个,每个nodeid相同的点之间是一个完全图。而在其外部,则很清晰的构建出一个邻接矩阵。比如1号线和2号线都有1节点,而1号线的1节点与2节点邻接,则在邻接矩阵中是1号线的1节点与1号线的2节点邻接,去其他线路的都没有任何关系。这样各条path是独立的,其产生的联系必须通过换乘,且是在同一个nodeid,不用的pathid之间,这种内部完全图之间的换乘。
通过这样拆分,可以将整个图构建成一个新图,这个图上,从fromnode出发,需要注意的是fromnode是一个int的变量,而新图的每个节点都是一个有着nodeid,和pathid的新的类。因此fromnode上可能是在多条线,或者说一个fromnode可以对应多个node,这种关系可以用hashmap进行表达。而在遍历时候,我们是随机选择的一个Node进行出发的,这是否意味着需要将fromnode对应的所有Node都遍历呢?事实上并不需要。我们只用在出发的时候将每个nodeid为fromnode的点加入到最终集合,并权值标定为0 ,这时候,从哪个地方出发还重要吗?已经不再重要了。
由此可以通过一次dijkstra,得到单源的所有最短路径,对其传入不同的参数,可以得到不同的结果,不满意度,票价等等,都可以被解决。
不幸的是,这样的设计写出了很多bug,稍后会进行分析。
对于连通块,设想的是构造一个node集合的副本,既然最短路径用的是bfs,不妨就让其bfs到结束,并删除这个副本的元素,直到这个副本的size为0,也就是所有的都被遍历了,否则可以对连通块个数加1。
然而不幸的是,又出现了bug。
由于图太大,难以放下,故不在此放第三次作业的类图了。
bug相关
在前两次作业中,由于课后进行了多次对拍,未发现bug。复杂度控制也比较好,超时情况也未出现。
在第三次作业,分别出现了一个bug和tle的情况。由于第三次作业的时间线把握不准,直到周二下午才将整体写完,通过中测,并开始对拍。期间发现几个bug进行了修改,但最后还是遗留下来了bug。
bug主要出现在对连通块的处理上,我的处理采用的是删除式的处理,而当size不为0,就添加1。这个步骤是在isconnect函数作业,起初想的是毕竟遍历完了,不为0,这时候的起点应该不会是原来的起点,这的确如此,因为原来的从那个起点出发的所有邻接点都已经被缓存下来,而我将其引申为应该不是原来被删除集合中的点了。这样便出现了问题,如果有两条毫不相干的路径,在第一条路径随便两个点isconnect后,第一条路径在集合中都被移除,这时候size不为0。如果换一个起点,还是第一条路径,此时未被缓存,将再次给连通块加1,而此时它根本不在目前的集合中,因此产生了bug。其修改是判断size不为0,同时,fromnode还需要在当前的还没删除的集合中,否则就不加1。
很明显这是由于我没有很好的控制好思维,产生了部分偏差,而在强测中,由于运气好,只有一个点涉及到了这种情形,但在互测中却被hack20次之多。后来我仔细的想了想为什么对拍没发现,强测也只出现一个bug。事实上这个bug是很严重的,其主要原因在于往往我们add的时候,都是连通的,很少有不连通的,每次isconnect,集合的size便为0,故不会多次添加,这是由于数据的针对性导致的。只要数据有针对性一些,立刻会被发现。
说完了wa,下面聊聊tle。
在我的架构里,tle的出现来源于很多情况,我的拆点复杂,且需要在终止点的时候进行一下遍历,然后每次换乘也需要遍历当前的nodeid对应的所有节点的最小的那个,这很费时间。因此在互测中会超时,而解决方案是采用优先级队列,可以降低一些时间,但仍不能降低到与大神们一样的地步。
在修复bug阶段,我很轻易的修复了wa的bug和tle的部分bug,但对于完全随机的疯狂的最短路径探索,我仍然会超时,因此最后还有一个bug未能修复,修复这个bug则需要更改自己的架构,且大改,权衡后故放弃。
心得体会
OO作业已经做了十二次了,其中有9次代码训练,三次博客作业,马上就要接近尾声,我的代码也逐渐从bug很多,到目前bug没那么多了。写出高质量的代码,一直以来是我们的追求,当然我知道,我离那里还有很远很远,但不论怎么说,进步总是有的,我总能看见他。求导的三次作业,一次未进互测,一次C组。电梯作业,一次A组,两次B组,没有任何正确性问题。规格单元,两次A组,一次B组,有一个正确性问题。逐渐的,我的平均水平在提高,希望在未来,的最后一个单元,可以完好的结局,虽然是一个不好的开头,但我仍期待有一个好的结尾。