我尝试使用MapMaker / CacheBuilder创建缓存,但我不明白如何正确处理空值。
ConcurrentMap<Key, Graph> graphs = new MapMaker()
.concurrencyLevel(4)
.weakKeys()
.maximumSize(10000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.makeComputingMap(
new Function<Key, Graph>() {
public Graph apply(Key key) {
return createExpensiveGraph(key);
}
});
如果createExpensiveGraph方法返回null值,则抛出NullpointerException。 我不明白为什么ComputingConcurrentHashMap会抛出一个NPE而不是只返回一个空值。
如何妥善处理?只是捕获NPE并返回null? 我错过了什么吗?
答案 0 :(得分:47)
Guava试图强迫您尽可能避免使用null
,因为null
存在的不正确或无证件行为会导致大量混淆和错误。我认为尽可能避免使用空值绝对是一个好主意,如果你可以修改你的代码使它不使用null
,我强烈推荐这种方法。
您的问题的答案关键取决于“null”值在您的应用程序中实际意味着什么。最有可能的是,这意味着这个密钥“没有价值”,或“没有任何东西”。在这种情况下,最好的选择是使用Optional
,使用Optional
。并使用Optional.absent()
而不是null
包含非空值。如果必须将其转换为null或非null值,则可以使用Optional.orNull()
。
答案 1 :(得分:11)
请注意,即使在您的问题的完整陈述中,您仍然不清楚您的意图是否要缓存该空值。无论CacheBuilder是否决定缓存空值,它都会让许多预期相反的用户感到惊讶。再次,null
会产生歧义(这就是它最擅长的!)。
所以这就是你做的。
在没有createExpensiveGraph
的全部费用的情况下,是否可以确定答案为“空”?即,真的只是一个简单的前提条件检查吗?如果是这样,你应该在询问缓存之前这样做,此时是否应该缓存结果的问题就会消失。
您要缓存空值吗?然后按照路易斯的建议使用Optional<T>
。
否则,缓存无法完成为密钥召唤正确值的工作,相应的响应是从缓存加载器中抛出异常(如果这是程序员错误,则取消选中,否则检查)。这将提供您想要的行为。如果您抛出已检查的例外情况,请确保您在另一方使用的是Cache.get
,而不是Cache.getUnchecked
。
答案 2 :(得分:9)
请参阅Guava wiki上的LivingWithNullHostileCollections以获取有关如何处理此问题的一些想法。