1 package com.fasterxml.jackson.databind.util; 2 3 import java.io.*; 4 import java.util.concurrent.ConcurrentHashMap; 5 6 /** 7 * Helper for simple bounded maps used for reusing lookup values. 8 *<p> 9 * Note that serialization behavior is such that contents are NOT serialized, 10 * on assumption that all use cases are for caching where persistence 11 * does not make sense. The only thing serialized is the cache size of Map. 12 *<p> 13 * NOTE: since version 2.4.2, this is <b>NOT</b> an LRU-based at all; reason 14 * being that it is not possible to use JDK components that do LRU _AND_ perform 15 * well wrt synchronization on multi-core systems. So we choose efficient synchronization 16 * over potentially more efficient handling of entries. 17 *<p> 18 * And yes, there are efficient LRU implementations such as 19 * <a href="https://code.google.com/p/concurrentlinkedhashmap/">concurrentlinkedhashmap</a>; 20 * but at this point we really try to keep external deps to minimum. But perhaps 21 * a shaded variant may be used one day. 22 */ 23 public class LRUMap<K,V> 24 implements java.io.Serializable 25 { 26 private static final long serialVersionUID = 1L; 27 28 protected final transient int _maxEntries; 29 30 protected final transient ConcurrentHashMap<K,V> _map; 31 LRUMap(int initialEntries, int maxEntries)32 public LRUMap(int initialEntries, int maxEntries) 33 { 34 // We'll use concurrency level of 4, seems reasonable 35 _map = new ConcurrentHashMap<K,V>(initialEntries, 0.8f, 4); 36 _maxEntries = maxEntries; 37 } 38 put(K key, V value)39 public V put(K key, V value) { 40 if (_map.size() >= _maxEntries) { 41 // double-locking, yes, but safe here; trying to avoid "clear storms" 42 synchronized (this) { 43 if (_map.size() >= _maxEntries) { 44 clear(); 45 } 46 } 47 } 48 return _map.put(key, value); 49 } 50 51 /** 52 * @since 2.5 53 */ putIfAbsent(K key, V value)54 public V putIfAbsent(K key, V value) { 55 // not 100% optimal semantically, but better from correctness (never exceeds 56 // defined maximum) and close enough all in all: 57 if (_map.size() >= _maxEntries) { 58 synchronized (this) { 59 if (_map.size() >= _maxEntries) { 60 clear(); 61 } 62 } 63 } 64 return _map.putIfAbsent(key, value); 65 } 66 67 // NOTE: key is of type Object only to retain binary backwards-compatibility get(Object key)68 public V get(Object key) { return _map.get(key); } 69 clear()70 public void clear() { _map.clear(); } size()71 public int size() { return _map.size(); } 72 73 /* 74 /********************************************************** 75 /* Serializable overrides 76 /********************************************************** 77 */ 78 79 /** 80 * Ugly hack, to work through the requirement that _value is indeed final, 81 * and that JDK serialization won't call ctor(s) if Serializable is implemented. 82 * 83 * @since 2.1 84 */ 85 protected transient int _jdkSerializeMaxEntries; 86 readObject(ObjectInputStream in)87 private void readObject(ObjectInputStream in) throws IOException { 88 _jdkSerializeMaxEntries = in.readInt(); 89 } 90 writeObject(ObjectOutputStream out)91 private void writeObject(ObjectOutputStream out) throws IOException { 92 out.writeInt(_jdkSerializeMaxEntries); 93 } 94 readResolve()95 protected Object readResolve() { 96 return new LRUMap<Object,Object>(_jdkSerializeMaxEntries, _jdkSerializeMaxEntries); 97 } 98 } 99