Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom equals/hash when inserting key (Guava Cache)

Simply, I have to override the way in which the cache chooses the right key because some fields (e.g., timestamp, message id, etc.) shouldn't be considered when retrieving a key. I cannot modify the actual hash function of the key object because it is already used to recognize in my code.
Is it possibile with Guava Caches? And with a workaround?

This is my configuration:

CacheBuilder.newBuilder().maximumSize(CACHE_SIZE).recordStats().
    expireAfterWrite(DEFAULT_AGE, TimeUnit.DAYS).build(
    new CacheLoader<Request, Response>() {
        @Override
        public Response load(Request request) { 
            return request.getResponse();
        }
    });

And this is my hash function (used somewhere else in my code):

public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + code;
    result = prime * result + messageID; // <= **this one shouldn't evaluated**
    result = prime * result + Arrays.hashCode(payload);
    result = prime * result + (int) (timestamp ^ timestamp >>> 32); // <= **this one shouldn't evaluated**
    result = prime * result + (type == null ? 0 : type.hashCode());
    result = prime * result + version;
    return result;
}

Btw, is this kind of cache using its own implementation of hash function (e.g., through introspection) or is it using the default one?

** EDIT: **
As pointed out in the responses, the best way to achieve this result is a wrapper class.
My solution:

/**
 * Nested class to store a request as a key in the cache. It is needed to
 * normalize the variable fields of the normal requests.
 */
private static final class CachedRequest extends Request {

    private static CachedRequest fromRequest(Request request) {
        // set only the fields that cannot change between two same requests
        // ...
    }

    @Override
    public int hashCode() {
        HashFunction hashFunction = Hashing.md5();
        HashCode hashCode;
        // ...
        return hashCode.asInt();
    }

    @Override
    public boolean equals(Object obj) {
            // coherent with hashCode()
            // ...
    }
}
like image 689
Hamal000 Avatar asked Sep 05 '12 14:09

Hamal000


People also ask

What is guava caching in Java?

Guava - Caching Utilities. Guava provides a very powerful memory based caching mechanism by an interface LoadingCache<K,V>. Values are automatically loaded in the cache and it provides many utility methods useful for caching needs.

Does guava cache throw exceptions when loading null values?

By default, Guava Cache will throw exceptions if you try to load a null value – as it doesn't make any sense to cache a null. But if null value means something in your code, then you can make good use of the Optional class as in the following example: 7.

What are the equals () and hashCode () methods in Java?

The Object class defines both the equals () and hashCode () methods – which means that these two methods are implicitly defined in every Java class, including the ones we create: We would expect income.equals (expenses) to return true.

How is hashCode calculated in HashMap?

When an object (Say a String "John") is added to the HashMap with a key (Say '1') using the put () method, So, the HashCode is calculated based on the key (i.e. '1' ).


2 Answers

You could simply wrap your Request objects into CachedRequest objects, where CachedRequest would implement hashCode() and equals() based on the desired fields, and provide access to the wrapped Request.

like image 98
JB Nizet Avatar answered Oct 22 '22 02:10

JB Nizet


I'm pretty sure, it's not possible with Guava. There are some legitimate cases for using a custom Equivalence, but they say they're too rare to be handled by the CacheBuilder and MapMaker.1 There's even com.google.common.base.Equivalence, but it gets used only internally (see also here and here).

You need to make you own key out of the fields you want to use for the lookup, or wrap your Request in another object defining equals and hashCode the way you want.

1The only case when something different from the default equals/hashCode gets used is with weakKeys (softKeys exists no more), and then it's the ==/System.identityHashCode combo. In no case you can choose the equivalence freely.

like image 25
maaartinus Avatar answered Oct 22 '22 01:10

maaartinus