有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

循环条件下方法调用的java效率

我正在编写一个游戏引擎,其中ArrayList中的一组对象使用for循环进行迭代。显然,效率非常重要,所以我想知道循环的效率

for (String extension : assetLoader.getSupportedExtensions()) {
    // do stuff with the extension here
}

其中getSupportedExtension()返回ArrayList{}的ArrayList。我想知道的是,是否每次循环迭代新扩展时都调用该方法。如果是这样的话,采取以下措施是否更有效:

ArrayList<String> supportedExtensions = ((IAssetLoader<?>) loader).getSupportedExtensions();

for (String extension : supportedExtensions) {
    // stuff
}

??提前谢谢


共 (3) 个答案

  1. # 1 楼答案

    否,方法assetLoader.getSupportedExtensions()在循环的第一次迭代之前只调用一次,用于创建增强for循环使用的Iterator<String>

    这两个代码段将具有相同的性能

  2. # 2 楼答案

    根据规范,成语

    for (String extension : assetLoader.getSupportedExtensions()) {
      ...
    }
    

    扩展到

    for (Iterator<String> it = assetLoader.getSupportedExtensions().iterator(); it.hasNext();)
    {
        String extension = it.next();
        ...
    }
    

    因此,您询问的呼叫仅在循环初始时间发生一次。它是迭代器对象,其方法被反复调用

    然而,如果你真的对你的应用程序的性能感兴趣,那么你应该确保你关注的是大的胜利,而不是像这样的小人物。在任何一段代码中,让getter调用成为瓶颈几乎是不可能的。对于运行在HotSpot上的应用程序,这将内联getter调用,并将其转换为直接字段访问

  3. # 3 楼答案

    1. 直接成本

    正如人们之前所说,因为

    for (String extension : assetLoader.getSupportedExtensions()) {
      //stuff
    }
    

    转化为

    for (Iterator<String> it = assetLoader.getSupportedExtensions().iterator(); it.hasNext();) {
        String extension = it.next();
        //stuf
    }
    

    getSupportedExtensions()只调用一次,并且两个代码段的性能成本相同,但由于

    1. 间接成本

    这是实例化和使用新的短期对象的成本+方法next()的成本。方法iterator()准备迭代器的实例。因此,需要花时间实例化对象,然后(当对象变得不可访问时)对其进行GC。总的间接成本不算太高(大约10条为新对象分配内存的指令+一些构造函数指令+大约5行ArrayList.Itr.next()+在次要GC上从Eden删除对象),但我个人更喜欢索引(甚至是普通数组):

    ArrayList<String> supportedExtensions = ((IAssetLoader<?>) loader).getSupportedExtensions();
    
    for (int i = 0; i < supportedExtensions.size(); i++) {
        String extension = supportedExtensions.get(i);
        // stuff
    }
    

    当我必须在应用程序的主路径中频繁地遍历列表时,进行迭代。具有隐藏成本的标准java代码的其他一些示例包括一些字符串方法(substring()、trim()等)、NIO选择器、将原语装箱/拆箱以将其存储在集合中等