Ehcache - using List <Integer> as cache value

So, here is the problem I'm trying to solve - I have an Object with two integer fields that I want to cache

public class MyObject { int x; int y; .... } 

Now the x field is what I basically map, but there may be duplicates, in which case I want to go back to the second field (so this.x = that.x and this.y = that. Y). y can be only 25 different values. Now I know that I could just combine the two as a String and use them as a cache key, but then I would have to try x+[25 possible values] to determine if the cache cache was very expensive. I was thinking of trying to keep List<Integer> as the cache value for the field x , and then if there were more than one, repeat this list and find the match on y .

Now, if I use a ConcurrentList (or a set, if I adore duplicates, ignores it at the moment), can I add several threads to it, and then return them back to the cache without race conditions? Is it possible that Ehcache can return two different list objects in two streams, and then when they add a new value to the list and try to return it to the cache, I could get undefined results? Do you see a better way to build this cache?

EDIT: I appreciate the answers below, but everyone seems to be missing the point. Will this work? Can Ehcache actually return two different objects for the same cache (say, if the object was on disk during a call and it serialized it twice, once for each call).

+6
java algorithm caching ehcache
source share
5 answers

It is entirely possible that you will get two different copies of your list (or any Serializable)! Try the following:

 public static void main(final String[] args) throws Exception { final Cache cache = CacheManager.getInstance().getCache("smallCache"); final List<String> list = new ArrayList<String>(); cache.put(new Element("A", list)); /* We put in a second element. Since maxElementsInMemory="1", this means * that "A" will be evicted from memory and written to disk. */ cache.put(new Element("B", new ArrayList<String>())); Thread.sleep(2000); // We need to wait a bit, until "A" is evicted. /* Imagine, the following happens in Thread 1: */ final List<String> retrievedList1 = (List<String>) cache.get("A").getValue(); retrievedList1.add("From Thread 1"); /* Meanwhile, someone puts something in the cache: */ cache.put(new Element("C", new ArrayList<String>())); Thread.sleep(2000); // Once again, we wait a bit, until "A" is evicted. /* Now the following happens in Thread 2: */ final List<String> retrievedList2 = (List<String>) cache.get("A").getValue(); retrievedList2.add("From Thread 2"); cache.put(new Element("A", retrievedList2)); /* Meanwhile in Thread 1: */ cache.put(new Element("A", retrievedList1)); /* Now let see the result: */ final List<String> resultingList = (List<String>) cache.get("A").getValue(); for (final String string : resultingList) { System.out.println(string); } /* Prints only "From Thread 1". "From Thread 2" is lost. But try it with maxElementsInMemory="3", too!! */ CacheManager.getInstance().shutdown(); } 

In ehcache.xml, I used the following:

 <cache name="smallCache" maxElementsInMemory="1" eternal="true" overflowToDisk="true" diskPersistent="true" maxElementsOnDisk="200" memoryStoreEvictionPolicy="LRU" transactionalMode="off" > </cache> 

One solution might be to use Explicit Blocking , which seems to be available for standalone (non-Terracotta) caches (with ehcache 2.1).

Another solution would be only one thread that can modify the list. If you have several threads that can change it, and you do not use a cache lock, you can get exactly the vague results that you described!

+4
source share

I have a different approach for you, which I just read in an article about finding a geographic range.

Put two key-value pairs in the cache: one with only x as the key and one with x and y as the key. When you look in the cache, first find x-and-y. If he is there, you have found the perfect match. If not, find the key x and maybe find a match with another y value.

+2
source share

I would create a method to get the value for your object. Use a semaphore to restrict access to the method (or use synchronized).

In your method, check for match only for X, and if it returns multiple results, the text for matches is XY.

As soon as the object is outside the cache, any changes to the object will also modify the object in the cache (since they point to the same instance).

If you want to be very careful, use synchronized methods to get / set member variables in MyObject and enable the lock, which is an instance of MyObject.

 public void setX( int x ) { synchronized( this ) { this.x = x; } } 
+1
source share

You can use a map containing a sorted set as a value. The first map can be indexed on X, and then you can select the first element from the sorted set, where the sorting is based on Y.

I think there are a lot of neat things in the google api collection that you could use, like SortedSetMultimap:

http://google-collections.googlecode.com/svn/trunk/javadoc/com/google/common/collect/SortedSetMultimap.html

0
source share
  • Make the key class x and y , i.e. class Key { int x,y }
  • perform a separate comparison operation for you "lexical ordering" on x and y ,
  • enter Map<Key,Value>
0
source share

All Articles