使用Guava MapMaker / CacheBuilder处理空值

我尝试使用MapMaker / CacheBuilder创建缓存,但我不明白如何正确处理空值。

ConcurrentMap graphs = new MapMaker() .concurrencyLevel(4) .weakKeys() .maximumSize(10000) .expireAfterWrite(10, TimeUnit.MINUTES) .makeComputingMap( new Function() { public Graph apply(Key key) { return createExpensiveGraph(key); } }); 

如果createExpensiveGraph方法返回null值,则抛出NullpointerException。 我不明白为什么ComputingConcurrentHashMap会抛出一个NPE而不是只返回一个空值。

如何妥善处理? 只是捕获NPE并返回null? 我错过了什么吗?

Guava试图强迫你尽可能避免使用null ,因为在null存在的情况下不正确或未记录的行为会导致大量的混淆和错误。 我认为尽可能避免使用空值绝对是一个好主意,如果你可以修改你的代码使它不使用null ,我强烈推荐这种方法。

您的问题的答案关键取决于“null”值在您的应用程序中实际意味着什么。 最有可能的是,这意味着这个密钥“没有价值”,或“没有任何东西”。 在这种情况下,您最好的选择是使用Optional ,使用Optional .of包装非空值并使用Optional.absent()而不是null 。 如果必须将其转换为null或非null值,则可以使用Optional.orNull()

请注意,即使在您的问题的完整陈述中,您仍然不清楚您的意图是否要缓存该空值。 无论CacheBuilder是否决定缓存空值,它都会让许多预期相反的用户感到惊讶。 再一次, null会产生歧义(这就是它最擅长的!)。

所以这就是你做的。

  1. 如果没有createExpensiveGraph的全部费用,是否可以确定答案是“null”? 即,真的只是一个简单的前提条件检查吗? 如果是这样,你应该在询问缓存之前这样做,此时是否应该缓存结果的问题就会消失。

  2. 要缓存空值吗? 然后按照路易斯的建议使用Optional

  3. 否则,缓存无法完成为密钥召唤正确值的工作,并且相应的响应是从缓存加载器中抛出exception(如果这是程序员错误,则取消选中,否则检查)。 这将提供您想要的行为。 如果抛出已检查的exception,请确保在另一侧使用Cache.get ,而不是Cache.getUnchecked

请参阅Guava wiki上的LivingWithNullHostileCollections ,了解如何处理此问题。