地图查找性能

时间:2013-08-30 14:14:18

标签: java performance collections

仅当地图包含给定的密钥时,我才想使用给定密钥的地图值执行某些操作。天真的我会写:

Map<String, String> myMap = ...;

if(myMap.containsKey(key)) {
  String value = myMap.get(key);

  // Do things with value
}

上面的代码看起来很容易理解,但从性能的角度来看,下面的代码会不会更好?

Map<String, String> myMap = ...;

String value = myMap.get(key);

if(value != null) {
  // Do things with value
}

在第二个片段中,我不喜欢value声明范围更广的事实。

给定案例的表现如何相对于地图实施而改变?

注意:我们假设地图中不允许使用空值。

3 个答案:

答案 0 :(得分:6)

Map是一个接口,因此实现类在实现每个操作方面有相当大的自由(完全有可能编写一个缓冲最后一个条目的类,这可能允许持续时间访问get操作,如果它与最后获得的对象相同,使两者几乎等效,除了可能需要的比较)。

对于TreeMapHashMapcontainsKey基本上只是get操作(更具体地说是getEntry),并检查{{1} }}

因此,对于这两个容器,第一个版本应该花费大约两倍(假设您在两种情况下都使用相同类型的null)。

注意Map是O(1)(散列函数非常适合数据),HashMap.get是O(log n)。因此,如果你在循环中做了大量的工作,并且TreeMap.get不包含数百万个元素的顺序,性能的差异可能是微不足道的。< / p>

但请注意MapMap.get的免责声明:

  

如果此映射允许空值,则返回值null不一定表示映射不包含该键的映射;地图也可能将键明确映射为null。 containsKey操作可用于区分这两种情况。

答案 1 :(得分:2)

回答你的问题
“对于Map实施,给定案例的表现如何变化?” 性能差异可以忽略不计。

评论您的评论
“在第二个片段中,我不喜欢声称价值范围更广的事实。” 好,你不应该。你知道,有两种方法可以从地图返回null:

  1. 密钥不存在 OR
  2. 密钥确实存在,但其值为null(如果Map实现允许空值,如HashMap)。
  3. 因此,如果密钥存在空值,那么这两个场景实际上可能会有不同的结果!

    修改

    我编写了以下代码来测试两种方案的性能:

    public class TestMapPerformance {
    
        static Map<String, String> myMap = new HashMap<String, String>();
        static int iterations = 7000000;
    
        // populate a map with seven million strings for keys
        static {
            for (int i = 0; i <= iterations; i++) {
                String tryIt = Integer.toString(i);
                myMap.put(tryIt, "hi");
            }
        }
        // run each scenario twice and print out the results.
        public static void main(String[] args) {
            System.out.println("Key Exists: " + testMap_CheckIfKeyExists(iterations));
            System.out.println("Value Null: " + testMap_CheckIfValueIsNull(iterations));
            System.out.println("Key Exists: " + testMap_CheckIfKeyExists(iterations));
            System.out.println("Value Null: " + testMap_CheckIfValueIsNull(iterations));
        }
    
        // Check if the key exists, then get its value  
        public static long testMap_CheckIfKeyExists(int iterations) {       
            Date date = new Date();
            for (int i = 0; i <= iterations; i++) {
                String key = Integer.toString(i);
                if(myMap.containsKey(key)) {
                    String value = myMap.get(key);
                    String newString = new String(value);
                }
            }
            return new Date().getTime() - date.getTime();
        }
    
        // Get the key's value, then check if that value is null
        public static long testMap_CheckIfValueIsNull(int iterations) {
            Date date = new Date();
            for (int i = 0; i <= iterations; i++) {
                String key = Integer.toString(i);
                String value = myMap.get(key);
                if(value != null) {
                    String newString = new String(value);
                }
            }
            return new Date().getTime() - date.getTime();
        }
    
    }
    

    我跑了,这就是结果:

    Key Exists: 9901
    Value Null: 11472
    Key Exists: 11578
    Value Null: 9387
    

    总之,性能上的差异可以忽略不计。

答案 2 :(得分:1)

显然第二个版本性能更高:你只在地图中查找一次键,而在第一个版本中你查找它两次因此计算两次键的哈希码并查看hashbuckets,假设你使用的是当然是hashmap。

你可以有一个完全不同的Map接口实现,它能够通过记住链接到最后一个包含方法调用中的键的映射条目来更好地处理这种代码,如果后续的get使用使用相同的键(使用==运算符),您可以立即从记住的映射条目中返回关联的值。

然而,第二种方法存在危险:如果我把它放在地图上怎么办:

map.put("null", null);

然后map.get(“null”)将返回null并且您将其视为“null”未映射,而map.contains(“null”)将返回true!