All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.semanticweb.elk.util.collections.NQEvictor Maven / Gradle / Ivy

The newest version!
/*-
 * #%L
 * ELK Utilities Collections
 * $Id:$
 * $HeadURL:$
 * %%
 * Copyright (C) 2011 - 2017 Department of Computer Science, University of Oxford
 * %%
 * Licensed 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.
 * #L%
 */
package org.semanticweb.elk.util.collections;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;

import org.semanticweb.elk.util.statistics.Stat;

import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;

/**
 * Acts as a sequence of {@link RecencyEvictor} (called levels) where if element
 * being added is in some of them, it is removed and added to the next level, or
 * if there is no next level, it is readded to the last one. Elements that are
 * in none of the levels are added to the first one. If there is only one level,
 * this evictor works as {@link RecencyEvictor}.
 * 

* This is a straight-forward generalization of Segmented LRU cache * <https://en.wikipedia.org/wiki/Cache_replacement_policies#Segmented_LRU_.28SLRU.29> * * @author Peter Skocovsky * * @param * The type of the elements. */ public class NQEvictor extends AbstractEvictor { private static final int DEFAULT_ELEMENTS_CAPACITY_ = Builder.DEFAULT_CAPACITY; private static final float DEFAULT_ELEMENTS_LOAD_FACTOR_ = 0.75f; private final List> elements_; private final List capacities_; private final List loadFactors_; NQEvictor(final List capacities, final List loadFactors) { this.elements_ = new ArrayList>( capacities.size()); for (int i = 0; i < capacities.size(); i++) { this.elements_.add( new LinkedHashMap(DEFAULT_ELEMENTS_CAPACITY_, DEFAULT_ELEMENTS_LOAD_FACTOR_, true)); } this.capacities_ = capacities; this.loadFactors_ = loadFactors; this.stats = new Stats(); } @Override public void add(final E element) { // If element is in some queue but the last one, upgrade it. for (int i = 0; i < elements_.size() - 1; i++) { final LinkedHashMap iThQueue = elements_.get(i); if (iThQueue.remove(element) != null) { elements_.get(i + 1).put(element, true); return; } } // else if element is in the last queue, reinsert it. final LinkedHashMap lastQueue = elements_ .get(elements_.size() - 1); if (lastQueue.containsKey(element)) { lastQueue.put(element, true); return; } // else put it on the first queue. elements_.get(0).put(element, true); } @Override public Iterator evict(final Predicate retain) { Preconditions.checkNotNull(retain); /* * Elements evicted from some level are added to the lower level. Only * elements evicted from the first level are actually completely * evicted. */ for (int i = capacities_.size() - 1; i > 0; i--) { final LinkedHashMap level = elements_.get(i); final LinkedHashMap lowerLevel = elements_.get(i - 1); final int capacity = capacities_.get(i); final double loadFactor = loadFactors_.get(i); if (level.size() <= capacity) { // Evict nothing. continue; } // else final int goalCapacity = (int) (capacity * loadFactor); final Iterator iterator = level.keySet().iterator(); while (iterator.hasNext() && level.size() > goalCapacity) { final E element = iterator.next(); lowerLevel.put(element, true); iterator.remove(); } } final List evicted = new ArrayList(); final LinkedHashMap firstLevel = elements_.get(0); final int capacity = capacities_.get(0); final double loadFactor = loadFactors_.get(0); if (firstLevel.size() <= capacity) { // Evict nothing. return evicted.iterator(); } // else final int goalCapacity = (int) (capacity * loadFactor); final Iterator iterator = firstLevel.keySet().iterator(); while (iterator.hasNext() && firstLevel.size() > goalCapacity) { final E element = iterator.next(); if (!retain.apply(element)) { evicted.add(element); iterator.remove(); } } return evicted.iterator(); } public int getN() { return capacities_.size(); } public int getCapacity(final int index) { return capacities_.get(index); } public int setCapacity(final int index, final int capacity) { if (0 > capacity) { throw new IllegalArgumentException("Capacity cannot be negative!"); } return capacities_.set(index, capacity); } protected static abstract class ProtectedBuilder> { public static final int DEFAULT_CAPACITY = 128; public static final double DEFAULT_LOAD_FACTOR = 0.75; protected final List capacities = new ArrayList(); protected final List loadFactors = new ArrayList(); /** * Adds a level with the specified capacity and load factor. The * arguments have the same meaning as if the level was a * {@link RecencyEvictor}. * * @param capacity * the capacity of the added level * @param loadFactor * the load factor of the added level * @return This builder. * @throws IllegalArgumentException * When capacity is negative or load factor is not between 0 * and 1 inclusive. */ public B addLevel(final int capacity, final double loadFactor) throws IllegalArgumentException { if (0 > capacity) { throw new IllegalArgumentException( "Capacity cannot be negative!"); } if (0 > loadFactor || loadFactor > 1) { throw new IllegalArgumentException( "Load factor must be between 0 and 1 inclusive!"); } capacities.add(capacity); loadFactors.add(loadFactor); return convertThis(); } /** * Adds a level with the specified capacity and the default load factor * {@link #DEFAULT_LOAD_FACTOR}. The arguments have the same meaning as * if the level was a {@link RecencyEvictor}. * * @param capacity * the capacity of the added level * @return This builder. * @throws IllegalArgumentException * When capacity is negative. */ public B addLevel(final int capacity) throws IllegalArgumentException { return addLevel(capacity, DEFAULT_LOAD_FACTOR); } /** * Adds a level with the default capacity {@link #DEFAULT_CAPACITY} and * the specified load factor. The arguments have the same meaning as if * the level was a {@link RecencyEvictor}. * * @param loadFactor * the load factor of the added level * @return This builder. * @throws IllegalArgumentException * When load factor is not between 0 and 1 inclusive. */ public B addLevel(final double loadFactor) throws IllegalArgumentException { return addLevel(DEFAULT_CAPACITY, loadFactor); } /** * Adds a level with the default capacity {@link #DEFAULT_CAPACITY} and * load factor {@link #DEFAULT_LOAD_FACTOR}. The arguments have the same * meaning as if the level was a {@link RecencyEvictor}. * * @return This builder. */ public B addLevel() { return addLevel(DEFAULT_CAPACITY); } /** * Builds the {@link NQEvictor} with the added levels. If none were * added, builds {@link NQEvictor} with one level with default capacity * and load factor. * * @param * The type of the elements maintained by the evictor. * * @return The built {@link NQEvictor} */ public NQEvictor build() { if (capacities.isEmpty()) { addLevel(); } return new NQEvictor(capacities, loadFactors); } protected abstract B convertThis(); } public static class Builder extends ProtectedBuilder implements Evictor.Builder { @Override protected Builder convertThis() { return this; } public static Builder valueOf(final String value) { final String[] args = Evictors.parseArgs(value, NQEvictor.class, null); if (args.length % 2 != 0) { throw new IllegalArgumentException( "The number of arguments must be even! value: " + value); } final Builder builder = new Builder(); for (int i = 0; i < args.length; i += 2) { final String capacityArg = args[i].trim(); final String loadFactorArg = args[i + 1].trim(); final int capacity = capacityArg.isEmpty() ? DEFAULT_CAPACITY : Integer.valueOf(capacityArg); final double loadFactor = loadFactorArg.isEmpty() ? DEFAULT_LOAD_FACTOR : Double.valueOf(loadFactorArg); builder.addLevel(capacity < 0 ? Integer.MAX_VALUE : capacity, loadFactor); } return builder; } @Override public String toString() { final StringBuilder result = new StringBuilder( NQEvictor.class.getName()).append("("); for (int i = 0; i < capacities.size(); i++) { if (i > 0) { result.append(","); } result.append(capacities.get(i)).append(",") .append(loadFactors.get(i)); } return result.append(")").toString(); } } public static Builder builder() { return new Builder(); } // Stats. protected class Stats { @Stat public String levelSizes() { final StringBuilder result = new StringBuilder( "" + elements_.get(0).size()); for (int i = 1; i < elements_.size(); i++) { result.append("_").append(elements_.get(i).size()); } return result.toString(); } // Empty so far. } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy