• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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