001/** 002 * 003 * Copyright 2003-2005 Jive Software, 2014-2016 Florian Schmaus 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.jxmpp.util.cache; 018 019import java.util.Collection; 020import java.util.LinkedHashMap; 021import java.util.Map; 022import java.util.Set; 023import java.util.concurrent.atomic.AtomicLong; 024 025/** 026 * A specialized Map that is size-limited (using an LRU algorithm). The Map is 027 * thread-safe. 028 * 029 * @author Matt Tucker 030 * @author Florian Schmaus 031 */ 032public class LruCache<K, V> extends LinkedHashMap<K, V> implements Cache<K, V> { 033 034 /** 035 * 036 */ 037 private static final long serialVersionUID = -4980809402073634607L; 038 039 /** 040 * The default initial value for the used data structures. 041 */ 042 private static final int DEFAULT_INITIAL_SIZE = 50; 043 044 /** 045 * Maximum number of items the cache will hold. 046 */ 047 private int maxCacheSize; 048 049 /** 050 * Maintain the number of cache hits and misses. A cache hit occurs every 051 * time the get method is called and the cache contains the requested 052 * object. A cache miss represents the opposite occurrence. 053 * <p> 054 * Keeping track of cache hits and misses lets one measure how efficient 055 * the cache is; the higher the percentage of hits, the more efficient. 056 */ 057 private final AtomicLong cacheHits = new AtomicLong(); 058 private final AtomicLong cacheMisses = new AtomicLong(); 059 060 /** 061 * Create a new cache and specify the maximum size of for the cache in 062 * bytes, and the maximum lifetime of objects. 063 * 064 * @param maxSize the maximum number of objects the cache will hold. -1 065 * means the cache has no max size. 066 */ 067 public LruCache(int maxSize) { 068 super(maxSize < DEFAULT_INITIAL_SIZE ? maxSize : DEFAULT_INITIAL_SIZE, 069 0.75f, true); 070 if (maxSize == 0) { 071 throw new IllegalArgumentException("Max cache size cannot be 0."); 072 } 073 this.maxCacheSize = maxSize; 074 } 075 076 @Override 077 protected final boolean removeEldestEntry(Map.Entry<K, V> eldest) { 078 return size() > maxCacheSize; 079 } 080 081 @Override 082 public final synchronized V put(K key, V value) { 083 return super.put(key, value); 084 } 085 086 @Override 087 public final V lookup(K key) { 088 return get(key); 089 } 090 091 @SuppressWarnings("deprecation") 092 @Deprecated 093 @Override 094 public final V get(Object key) { 095 V cacheObject; 096 synchronized (this) { 097 cacheObject = super.get(key); 098 } 099 if (cacheObject == null) { 100 // The object didn't exist in cache, so increment cache misses. 101 cacheMisses.incrementAndGet(); 102 return null; 103 } 104 105 // The object exists in cache, so increment cache hits. Also, increment 106 // the object's read count. 107 cacheHits.incrementAndGet(); 108 109 return cacheObject; 110 } 111 112 @Override 113 public final synchronized V remove(Object key) { 114 return super.remove(key); 115 } 116 117 @Override 118 public final void clear() { 119 synchronized (this) { 120 super.clear(); 121 } 122 cacheHits.set(0); 123 cacheMisses.set(0); 124 } 125 126 @Override 127 public final synchronized int size() { 128 return super.size(); 129 } 130 131 @Override 132 public final synchronized boolean isEmpty() { 133 return super.isEmpty(); 134 } 135 136 @Override 137 public final synchronized Collection<V> values() { 138 return super.values(); 139 } 140 141 @Override 142 public final synchronized boolean containsKey(Object key) { 143 return super.containsKey(key); 144 } 145 146 @Override 147 public final synchronized void putAll(Map<? extends K, ? extends V> m) { 148 super.putAll(m); 149 } 150 151 @Override 152 public final synchronized boolean containsValue(Object value) { 153 return super.containsValue(value); 154 } 155 156 @Override 157 public final synchronized Set<java.util.Map.Entry<K, V>> entrySet() { 158 return super.entrySet(); 159 } 160 161 @Override 162 public final synchronized Set<K> keySet() { 163 return super.keySet(); 164 } 165 166 /** 167 * Get the number of cache hits. 168 * 169 * @return the number of cache hits. 170 */ 171 public final long getCacheHits() { 172 return cacheHits.longValue(); 173 } 174 175 /** 176 * Get the number of cache misses. 177 * 178 * @return the number of cache misses. 179 */ 180 public final long getCacheMisses() { 181 return cacheMisses.longValue(); 182 } 183 184 @Override 185 public final int getMaxCacheSize() { 186 return maxCacheSize; 187 } 188 189 @Override 190 public final void setMaxCacheSize(int maxCacheSize) { 191 this.maxCacheSize = maxCacheSize; 192 } 193}