使用Guava MapMaker/CacheBuilder处理空值

Ant*_*ier 26 java guava

我尝试使用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);
             }
           });
Run Code Online (Sandbox Code Playgroud)

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

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

Lou*_*man 48

番石榴试图强迫你尽可能避免使用null,因为存在的不正当或无证的行为null会导致大量的混乱和错误.我认为尽可能避免使用空值绝对是一个好主意,如果你可以修改你的代码使它不使用null,我强烈推荐这种方法.

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

  • 如果您从其他函数获取null或非null值,您可能会发现使用Optional.fromNullable最简单,它会将null或非null值转换为相应的Optional. (3认同)

Kev*_*ion 12

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

所以这就是你做的.

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

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

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


小智 9

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