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
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
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 */
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
68 public V get(Object key) { return _map.get(key); }
69
70 public void clear() { _map.clear(); }
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
87 private void readObject(ObjectInputStream in) throws IOException {
88 _jdkSerializeMaxEntries = in.readInt();
89 }
90
91 private void writeObject(ObjectOutputStream out) throws IOException {
92 out.writeInt(_jdkSerializeMaxEntries);
93 }
94
95 protected Object readResolve() {
96 return new LRUMap<Object,Object>(_jdkSerializeMaxEntries, _jdkSerializeMaxEntries);
97 }
98 }
99