| package org.apache.lucene.facet.taxonomy.writercache; |
| |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.LinkedHashMap; |
| import org.apache.lucene.facet.taxonomy.FacetLabel; |
| |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /** |
| * An an LRU cache of mapping from name to int. |
| * Used to cache Ordinals of category paths. |
| * |
| * @lucene.experimental |
| */ |
| // Note: Nothing in this class is synchronized. The caller is assumed to be |
| // synchronized so that no two methods of this class are called concurrently. |
| class NameIntCacheLRU { |
| |
| private HashMap<Object, Integer> cache; |
| long nMisses = 0; // for debug |
| long nHits = 0; // for debug |
| private int maxCacheSize; |
| |
| NameIntCacheLRU(int maxCacheSize) { |
| this.maxCacheSize = maxCacheSize; |
| createCache(maxCacheSize); |
| } |
| |
| /** Maximum number of cache entries before eviction. */ |
| public int getMaxSize() { |
| return maxCacheSize; |
| } |
| |
| /** Number of entries currently in the cache. */ |
| public int getSize() { |
| return cache.size(); |
| } |
| |
| private void createCache (int maxSize) { |
| if (maxSize<Integer.MAX_VALUE) { |
| cache = new LinkedHashMap<Object, Integer>(1000,(float)0.7,true); //for LRU |
| } else { |
| cache = new HashMap<Object, Integer>(1000,(float)0.7); //no need for LRU |
| } |
| } |
| |
| Integer get (FacetLabel name) { |
| Integer res = cache.get(key(name)); |
| if (res==null) { |
| nMisses ++; |
| } else { |
| nHits ++; |
| } |
| return res; |
| } |
| |
| /** Subclasses can override this to provide caching by e.g. hash of the string. */ |
| Object key(FacetLabel name) { |
| return name; |
| } |
| |
| Object key(FacetLabel name, int prefixLen) { |
| return name.subpath(prefixLen); |
| } |
| |
| /** |
| * Add a new value to cache. |
| * Return true if cache became full and some room need to be made. |
| */ |
| boolean put (FacetLabel name, Integer val) { |
| cache.put(key(name), val); |
| return isCacheFull(); |
| } |
| |
| boolean put (FacetLabel name, int prefixLen, Integer val) { |
| cache.put(key(name, prefixLen), val); |
| return isCacheFull(); |
| } |
| |
| private boolean isCacheFull() { |
| return cache.size() > maxCacheSize; |
| } |
| |
| void clear() { |
| cache.clear(); |
| } |
| |
| String stats() { |
| return "#miss="+nMisses+" #hit="+nHits; |
| } |
| |
| /** |
| * If cache is full remove least recently used entries from cache. Return true |
| * if anything was removed, false otherwise. |
| * |
| * See comment in DirectoryTaxonomyWriter.addToCache(CategoryPath, int) for an |
| * explanation why we clean 2/3rds of the cache, and not just one entry. |
| */ |
| boolean makeRoomLRU() { |
| if (!isCacheFull()) { |
| return false; |
| } |
| int n = cache.size() - (2*maxCacheSize)/3; |
| if (n<=0) { |
| return false; |
| } |
| Iterator<Object> it = cache.keySet().iterator(); |
| int i = 0; |
| while (i<n && it.hasNext()) { |
| it.next(); |
| it.remove(); |
| i++; |
| } |
| return true; |
| } |
| |
| } |