考虑到以下两个代码选项,第二个代码选项是否有任何性能优势(在很大规模或很长一段时间内)?
选项1
private Map<Long, Animal> animals = ...;
public Map<Long, Animal> getAnimals() {
return animals;
}
public void useAnimals() {
for (int i=0; i < SOME_LARGE_NUMBER; i++) {
Animal animal = getAnimals().get(id);
}
// Many many calls to getAnimals() are made...
}
选项2 - 没有吸气剂
private Map<Long, Animal> animals = ...;
public void useAnimals() {
for (int i=0; i < SOME_NUMBER; i++) {
Animal animal = animals.get(id);
}
// No method calls made
}
如果它对性能有害,为什么,以及我该如何确定是否值得减轻?
而且,将getAnimals()
的结果存储为本地会提供一个好处......
SOME_NUMBER
是数百还是数千?SOME_NUMBER
的数量级仅为10?注意:我之前说过“封装”。我将其更改为“getter”,因为其目的实际上不是该字段无法修改,而是无法重新分配。封装只是为了从子类中删除赋值的责任。
答案 0 :(得分:7)
JVM很可能会在紧密循环中内联getAnimals()
调用,有效地回退到选项1 。所以不要打扰,这实际上是微观(纳米?)优化。
另一件事是从字段访问迁移到本地变量。这听起来不错,因为每次总是在堆栈上有一个引用(两次内存访问与一次)时,不要遍历this
引用。但是我相信(如果我错了,请纠正我),因为animals
是私有的而非volatile
,JVM将在运行时为您执行此优化。
答案 1 :(得分:5)
第二个片段比第一个片段更多封装。第一个允许访问任何人的内部映射,而第二个将它封装在类中。
两者都将带来可比的表现。
编辑:既然你改变了问题,我也会改变答案。如果你通过一个getter,并且getter不是final,那就意味着子类可能会返回另一个映射,而不是你在类中保存的映射。选择是否希望您的方法在子类的地图上或在类的地图上操作。两者都可以接受,具体取决于具体情况。
无论如何,假设您的子类总是制作地图的防御性副本,如果不将getter的结果缓存在useAnimals
的局部变量中,则最终会有许多副本。可能需要始终处理子类映射的最新值,但我怀疑是这种情况。
如果没有子类,或者子类没有覆盖该方法,或者通过总是返回相同的映射来覆盖它,两者都将导致相当的性能,你不应该关心它。
答案 2 :(得分:3)
您是否对此进行了分析以确定是否重要,对于现代JIT,我会猜测它会被忽略,特别是如果animals
被标记为final
但是什么都阻止你自己测试这个。
无论哪种方式,我都是100%,这将从不成为您应用程序中的瓶颈。
答案 3 :(得分:-2)
好吧,我不认为JVM会内联函数调用。所以它可能会影响性能。更好的方法是创建局部变量并为其分配类字段动物。