Eri*_*sen 85 java performance map code-readability
我一直想知道在最佳实践中是否允许不使用该containsKey()方法java.util.Map,而是对结果进行空检查get().
我的理由是,对值进行两次查找似乎是多余的 - 首先是for containsKey(),然后是for get().
另一方面,可能是大多数标准实现的Map高速缓存是最后一次查找,或者编译器可以以其他方式取消冗余,并且为了代码的可读性,优选维护该containsKey()部分.
我非常感谢你的评论.
Evg*_*eev 105
允许一些Map实现具有空值,例如HashMap,在这种情况下,如果get(key)返回null它并不保证在与该键相关联的映射中没有条目.
因此,如果您想知道地图是否包含密钥用途Map.containsKey.如果只需要一个映射到键使用的值Map.get(key).如果此映射允许空值,则返回值null不一定表示映射不包含该键的映射; 在这种情况下Map.containsKey是没用的,会影响性能.此外,在并发访问映射(例如ConcurrentHashMap)的情况下,在您测试之后,Map.containsKey(key)有可能在您调用之前该条目将被另一个线程删除Map.get(key).
ass*_*ias 39
我认为这是相当标准的:
Object value = map.get(key);
if (value != null) {
//do something with value
}
Run Code Online (Sandbox Code Playgroud)
代替
if (map.containsKey(key)) {
Object value = map.get(key);
//do something with value
}
Run Code Online (Sandbox Code Playgroud)
它的可读性和效率都不低,所以我没有看到任何不这样做的理由.显然,如果您的映射可以包含null,则这两个选项不具有相同的语义.
正如assylias指出的那样,这是一个语义问题.通常,Map.get(x)== null是您想要的,但有些情况下使用containsKey很重要.
一个这样的情况是缓存.我曾经在Web应用程序中处理性能问题,该应用程序经常查询其数据库以查找不存在的实体.当我研究该组件的缓存代码时,我意识到如果cache.get(key)== null则查询数据库.如果数据库返回null(未找到实体),我们将缓存该键 - > null映射.
切换到containsKey解决了这个问题,因为映射到空值实际上意味着什么.键映射到null具有与不存在的键不同的语义含义.
我们可以使用 Java8 可选使 @assylias 答案更具可读性,
Optional.ofNullable(map.get(key)).ifPresent(value -> {
//do something with value
};)
Run Code Online (Sandbox Code Playgroud)
小智 5
containsKeyget仅当我们先验地知道永远不会允许空值时,后跟 a才是多余的。如果 null 值无效,则调用 会造成containsKey重大的性能损失,并且只是开销,如下面的基准测试所示。
Java 8Optional习惯用法 -Optional.ofNullable(map.get(key)).ifPresent或者Optional.ofNullable(map.get(key)).ifPresent- 与单纯的空检查相比,会产生不小的开销。
AHashMap使用O(1)常量表查找,而 aTreeMap使用O(log(n))查找。在containsKey随后是get在调用时成语慢得多TreeMap。
见https://github.com/vkarun/enum-reverse-lookup-table-jmh
// t1
static Type lookupTreeMapNotContainsKeyThrowGet(int t) {
if (!lookupT.containsKey(t))
throw new IllegalStateException("Unknown Multihash type: " + t);
return lookupT.get(t);
}
// t2
static Type lookupTreeMapGetThrowIfNull(int t) {
Type type = lookupT.get(t);
if (type == null)
throw new IllegalStateException("Unknown Multihash type: " + t);
return type;
}
// t3
static Type lookupTreeMapGetOptionalOrElseThrow(int t) {
return Optional.ofNullable(lookupT.get(t)).orElseThrow(() -> new
IllegalStateException("Unknown Multihash type: " + t));
}
// h1
static Type lookupHashMapNotContainsKeyThrowGet(int t) {
if (!lookupH.containsKey(t))
throw new IllegalStateException("Unknown Multihash type: " + t);
return lookupH.get(t);
}
// h2
static Type lookupHashMapGetThrowIfNull(int t) {
Type type = lookupH.get(t);
if (type == null)
throw new IllegalStateException("Unknown Multihash type: " + t);
return type;
}
// h3
static Type lookupHashMapGetOptionalOrElseThrow(int t) {
return Optional.ofNullable(lookupH.get(t)).orElseThrow(() -> new
IllegalStateException("Unknown Multihash type: " + t));
}
Run Code Online (Sandbox Code Playgroud)
Benchmark (iterations) (lookupApproach) Mode Cnt Score Error Units MultihashTypeLookupBenchmark.testLookup 1000 t1 avgt 9 33.438 ± 4.514 us/op MultihashTypeLookupBenchmark.testLookup 1000 t2 avgt 9 26.986 ± 0.405 us/op MultihashTypeLookupBenchmark.testLookup 1000 t3 avgt 9 39.259 ± 1.306 us/op MultihashTypeLookupBenchmark.testLookup 1000 h1 avgt 9 18.954 ± 0.414 us/op MultihashTypeLookupBenchmark.testLookup 1000 h2 avgt 9 15.486 ± 0.395 us/op MultihashTypeLookupBenchmark.testLookup 1000 h3 avgt 9 16.780 ± 0.719 us/op
https://github.com/openjdk-mirror/jdk7u-jdk/blob/master/src/share/classes/java/util/TreeMap.java
https://github.com/openjdk-mirror/jdk7u-jdk/blob/master/src/share/classes/java/util/HashMap.java
| 归档时间: |
|
| 查看次数: |
49328 次 |
| 最近记录: |