Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handle null value using Guava MapMaker/CacheBuilder

Tags:

java

guava

I try to make a cache using MapMaker/CacheBuilder but I don't understand how to properly handle null values.

 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);
             }
           });

If the method createExpensiveGraph returns a null value, then a NullpointerException is thrown. I don't understand why the ComputingConcurrentHashMap throws a NPE instead of just returning a null value.

How to properly handle this ? Just catch the NPE and return null instead ? Am I missing something ?

like image 465
Anthony Chatellier Avatar asked Nov 28 '11 15:11

Anthony Chatellier


3 Answers

Guava tries to force you to avoid using null wherever possible, because improper or undocumented behavior in the presence of null can cause a huge amount of confusion and bugs. I think it's definitely a good idea to avoid using nulls wherever possible, and if you can modify your code so that it doesn't use null, I strongly recommend that approach instead.

The answer to your question critically depends on what a "null" value actually means in your application. Most likely, it means that there's "no value" for this key, or "nothing there." In this case, probably your best bet is to use Optional, wrapping non-null values with Optional.of and using Optional.absent() instead of null. If you must turn that into a null or non-null value, you can use Optional.orNull().

like image 133
Louis Wasserman Avatar answered Nov 16 '22 21:11

Louis Wasserman


Notice how even in the full statement of your question it's still not clear whether your intent is to have that null value cached or not. Whether CacheBuilder decided to cache null values or not, it would surprise many users who expected the opposite. Once again, null creates ambiguities (that's what it's best at!).

So here's what you do.

  1. Is it possible to determine that the answer will be "null" without the full expense of createExpensiveGraph? i.e., is there really just a simple precondition check going on? If so, you should do that before asking the cache at all, at which point the question of whether the result should be cached or not just goes away.

  2. Do you want to cache a null value? Then follow Louis's advice to use Optional<T>.

  3. Otherwise, the cache failed to do its job of summoning a correct value for the key, and the appropriate response is to throw an exception from your cache loader (unchecked if this is programmer error, checked otherwise). And that will provide the behavior you want. If you throw a checked exception, make sure you're using Cache.get, not Cache.getUnchecked, on the other side.

like image 12
Kevin Bourrillion Avatar answered Nov 16 '22 20:11

Kevin Bourrillion


See LivingWithNullHostileCollections on the Guava wiki for some ideas on how to deal with this.

like image 9
fry Avatar answered Nov 16 '22 20:11

fry