/*
 * Copyright 2014 Red Hat, Inc.
 *
 *  All rights reserved. This program and the accompanying materials
 *  are made available under the terms of the Eclipse Public License v1.0
 *  and Apache License v2.0 which accompanies this distribution.
 *
 *  The Eclipse Public License is available at
 *  http://www.eclipse.org/legal/epl-v10.html
 *
 *  The Apache License v2.0 is available at
 *  http://www.opensource.org/licenses/apache2.0.php
 *
 *  You may elect to redistribute this code under either of these licenses.
 */

package io.vertx.ext.web.impl;

import java.util.ArrayDeque;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiFunction;

Concurrent LRU cache. Note that remove operation on this structure is SLOW! Avoid using it.
Author:Tim Fox
/** * Concurrent LRU cache. * * Note that remove operation on this structure is SLOW! Avoid using it. * * @author <a href="http://tfox.org">Tim Fox</a> */
public class ConcurrentLRUCache<K, V> extends ConcurrentHashMap<K, V> { private int maxSize; private final Queue<K> queue = new ArrayDeque<>(); public ConcurrentLRUCache(int maxSize) { this.maxSize = maxSize; checkSize(); } public ConcurrentLRUCache(int initialCapacity, int maxSize) { super(initialCapacity); this.maxSize = maxSize; checkSize(); } public ConcurrentLRUCache(Map<? extends K, ? extends V> m, int maxSize) { super(m); this.maxSize = maxSize; checkSize(); for (K k: m.keySet()) { entryAdded(k); } checkRemoveOldest(); } public ConcurrentLRUCache(int initialCapacity, float loadFactor, int maxSize) { super(initialCapacity, loadFactor); this.maxSize = maxSize; checkSize(); } public ConcurrentLRUCache(int initialCapacity, float loadFactor, int concurrencyLevel, int maxSize) { super(initialCapacity, loadFactor, concurrencyLevel); this.maxSize = maxSize; checkSize(); } public void setMaxSize(int maxSize) { this.maxSize = maxSize; checkRemoveOldest(); } @Override public V put(K key, V value) { V v = super.put(key, value); if (v == null) { entryAdded(key); } checkRemoveOldest(); return v; } @Override public void putAll(Map<? extends K, ? extends V> m) { for (K k: m.keySet()) { if (!super.containsKey(k)) { entryAdded(k); } } super.putAll(m); checkRemoveOldest(); } @Override public V remove(Object key) { V v = super.remove(key); if (v != null) { entryRemoved(key); } return v; } @Override public void clear() { super.clear(); queue.clear(); } @Override public V putIfAbsent(K key, V value) { V v = super.putIfAbsent(key, value); if (v == null) { entryAdded(key); } checkRemoveOldest(); return v; } @Override public boolean remove(Object key, Object value) { boolean removed = super.remove(key, value); if (removed) { entryRemoved(value); } return removed; } @Override public V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) { throw new UnsupportedOperationException(); } private void entryAdded(K k) { queue.add(k); } private void entryRemoved(Object o) { if (!queue.remove(o)) { throw new IllegalStateException("Failed to remove"); } } private void checkRemoveOldest() { while (queue.size() > maxSize) { K k = queue.poll(); if (k != null) { super.remove(k); } } } private void checkSize() { if (maxSize < 1) { throw new IllegalArgumentException("maxSize must be >= 1"); } } public int queueSize() { return queue.size(); } }