Java:通过HashMap进行迭代,哪个更有效率?

2022-08-31 15:23:52

给定以下代码,使用两种替代方法来循环访问它,
这两种方法之间是否存在任何性能差异?

        Map<String, Integer> map = new HashMap<String, Integer>();
        //populate map

        //alt. #1
        for (String key : map.keySet())
        {
            Integer value = map.get(key);
            //use key and value
        }

        //alt. #2
        for (Map.Entry<String, Integer> entry : map.entrySet())
        {
            String key = entry.getKey();
            Integer value = entry.getValue();
            //use key and value
        }

我倾向于认为这是迭代整个过程的更有效的方法(但我可能是错的)alt. #2map


答案 1

您的第二个选项肯定更有效,因为与第一个选项中的n次相比,您只执行一次查找。

但是,没有什么比尽可能地尝试一下更好了。所以这里是 -

(不完美,但足以验证假设和我的机器)

public static void main(String args[]) {

    Map<String, Integer> map = new HashMap<String, Integer>();
    // populate map

    int mapSize = 500000;
    int strLength = 5;
    for(int i=0;i<mapSize;i++)
        map.put(RandomStringUtils.random(strLength), RandomUtils.nextInt());
    
    long start = System.currentTimeMillis();
    // alt. #1
    for (String key : map.keySet()) {
        Integer value = map.get(key);
        // use key and value
    }
    System.out.println("Alt #1 took "+(System.currentTimeMillis()-start)+" ms");
    
    start = System.currentTimeMillis();
    // alt. #2
    for (Map.Entry<String, Integer> entry : map.entrySet()) {
        String key = entry.getKey();
        Integer value = entry.getValue();
        // use key and value
    }
    System.out.println("Alt #2 took "+(System.currentTimeMillis()-start)+" ms");
}

结果(一些有趣的结果)

使用
Alt #1需要26毫秒
Alt #2需要20毫秒int mapSize = 5000; int strLength = 5;

使用
Alt #1需要32毫秒
Alt #2需要20毫秒int mapSize = 50000; int strLength = 5;

使用
Alt #1需要22毫秒
Alt #2需要21毫秒int mapSize = 50000; int strLength = 50;

使用
Alt #1需要28毫秒
Alt #2需要23毫秒int mapSize = 50000; int strLength = 500;

使用
Alt #1需要92毫秒
Alt #2需要57毫秒int mapSize = 500000; int strLength = 5;

...等等


答案 2

第二个代码段会稍微快一些,因为它不需要重新查找键。

所有迭代器都调用 nextEntry 方法,该方法返回 .HashMapEntry<K,V>

您的第一个代码段会丢弃条目中的值(在 KeyIterator 中),然后在字典中再次查找它。

您的第二个代码段直接使用键和值(来自 EntryIterator)

keySet()entrySet() 都是廉价的调用)