getter对Java的性能有害吗?

时间:2011-11-09 17:52:11

标签: java performance encapsulation

考虑到以下两个代码选项,第二个代码选项是否有任何性能优势(在很大规模或很长一段时间内)?

选项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”,因为其目的实际上不是该字段无法修改,而是无法重新分配。封装只是为了从子类中删除赋值的责任。

4 个答案:

答案 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会内联函数调用。所以它可能会影响性能。更好的方法是创建局部变量并为其分配类字段动物。