请考虑以下代码:
public class BasicBuildOrder {
private List<GameObject> gameObjects = new ArrayList<>();
private List<GameObject> savedState;
public void saveState() {
savedState = new ArrayList<>(gameObjects);
}
public void resetToSavedState() {
if (savedState == null) {
throw new IllegalStateException("the state has not been saved");
}
gameObjects = savedState;
}
public void add(GameObject gameObject) {
gameObjects.add(gameObject);
}
public void get(int index) {
return gameObjects.get(index);
}
public void remove(GameObject gameObject) {
gameObjects.remove(gameObject);
}
}
我正在实现一种模式,您可以将列表标记为在某个特定状态下保存,然后恢复该状态。我知道当前的保存/重置方式有点阴暗并且可以提供O(n)性能,而这可以通过标记已添加,检索和删除的项目来改进。
现在我看到的一个问题是,在调用resetToSavedState()
之前,对saveState()
的调用将完成,因此我需要检查一下。
我的问题是:如果if (savedState == null)
知道已调用saveState()
并因此savedState
,那么null
检查是否在某些时候被Java优化(由编译器或在运行时) }不能再是x-ms-request-charge
了吗?
另外一个问题,这个方法是否有一种模式可用于方法B之间的关系?
答案 0 :(得分:5)
将Java源代码编译为.class文件时无法优化。如果从同一方法调用saveState()
和resetToSavedState()
,并且在JIT编译期间内联两者(或者某些更深的调用链完全内联),它可以在运行时由JIT编译器进行优化。由于saveState()
和resetToSavedState()
非常简单,因此可以在此处内联。之后,两者都成为外部方法的一部分,在构建控制流图和值分析之后,可以确定没有控制路径进入if
语句,因此将修剪这样的CFG边缘。但是你不应该依赖这种情况。
通常,只能查看程序代码(不实际执行程序代码),您无法可靠地预测程序顺序。但是,额外的空检查在性能方面几乎没有任何意义,所以你不必担心它。
答案 1 :(得分:3)
if(savedState == null)是否在某些时候通过Java(由编译器或在运行时)进行优化,如果它知道已经调用了saveState(),那么savedState不能再为null?
JVM,而不是JIT都没有完全优化它(将来可能)。但是,CPU可以使用分支预测来假设检查将是错误的,因此几乎没有影响。即&lt; 1 ns。
答案 2 :(得分:2)
此“methodA-called-before-methodB relation
”有一种模式,您甚至可以将其编码到类型系统中。
考虑构建器的流体API。这似乎完美地封装了你的问题,甚至没有进行运行时检查:
public class BasicBuildOrder {
protected List<GameObject> gameObjects = new ArrayList<>();
public static class SavedBuildOrder extends BasicBuildOrder {
private List<GameObject> savedState;
private SavedBuildOrder(Collection<GameObject> gameObjects) {
savedState = new ArrayList<>(gameObjects);
}
public void resetToSavedState() {
gameObjects = savedState;
}
}
public SavedBuildOrder saveState() {
return new SavedBuildOrder(gameObjects);
}
public void add(GameObject gameObject) {
gameObjects.add(gameObject);
}
public void get(int index) {
return gameObjects.get(index);
}
public void remove(GameObject gameObject) {
gameObjects.remove(gameObject);
}
}
这种相当笨重的解决方法的替代方案类似于Memento Pattern。
无论如何,Tagir Valeev,Oliver Charlesworth和Filipp可能都是正确的:
然而,即使没有编译器,null检查也应该相当便宜 魔法。 - 菲利普
然而,额外的空检查在性能方面几乎没有任何意义, 所以你不应该担心它。 - Tagir
除非可以通过静态分析显示该方法始终如此 调用后,必须使用某种运行时信息。因此有些 必须进行检查。 - 奥利弗