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

org.semanticweb.elk.util.collections.RecencyEvictor 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.Collections;
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;

/**
 * Evicts least recent elements after the capacity is exceeded.
 * Eviction is trying to reduce the number of elements in this evictor to
 * capacity times load factor. However, it still does not evict the
 * elements that should be retained.
 * 
 * @author Peter Skocovsky
 *
 * @param 
 *            The type of the elements.
 */
public class RecencyEvictor extends AbstractEvictor {

	private static final int DEFAULT_ELEMENTS_CAPACITY_ = Builder.DEFAULT_CAPACITY;
	private static final float DEFAULT_ELEMENTS_LOAD_FACTOR_ = 0.75f;

	private final LinkedHashMap elements_ = new LinkedHashMap(
			DEFAULT_ELEMENTS_CAPACITY_, DEFAULT_ELEMENTS_LOAD_FACTOR_, true);
	private final double loadFactor_;

	private int capacity_;

	RecencyEvictor(final int capacity, final double loadFactor) {
		this.capacity_ = capacity;
		this.loadFactor_ = loadFactor;

		this.stats = new Stats();
	}

	@Override
	public void add(final E element) {
		elements_.put(element, true);
	}

	@Override
	public Iterator evict(final Predicate retain) {
		Preconditions.checkNotNull(retain);

		if (elements_.size() <= capacity_) {
			// Evict nothing.
			return Collections. emptyList().iterator();
		}
		// else

		final int goalCapacity = (int) (capacity_ * loadFactor_);
		final List evicted = new ArrayList(goalCapacity < elements_.size()
				? elements_.size() - goalCapacity : 0);
		final Iterator iterator = elements_.keySet().iterator();
		while (iterator.hasNext() && elements_.size() > goalCapacity) {
			final E element = iterator.next();
			if (!retain.apply(element)) {
				evicted.add(element);
				iterator.remove();
			}
		}

		return evicted.iterator();
	}

	public int getCapacity() {
		return capacity_;
	}

	public void setCapacity(final int capacity) {
		if (0 > capacity) {
			throw new IllegalArgumentException("Capacity cannot be negative!");
		}
		this.capacity_ = capacity;
	}

	public int size() {
		return elements_.size();
	}

	protected static abstract class ProtectedBuilder> {

		public static final int DEFAULT_CAPACITY = 128;
		public static final double DEFAULT_LOAD_FACTOR = 0.75;

		protected int capacity_ = DEFAULT_CAPACITY;
		protected double loadFactor_ = DEFAULT_LOAD_FACTOR;

		/**
		 * When the provided capacity is exceeded, elements will be evicted.
		 * Capacity not must be negative!
		 * 

* If not called, capacity defaults to {@link #DEFAULT_CAPACITY}. * * @param capacity * The capacity of the evictor. * @return This builder. * @throws IllegalArgumentException * When the argument is negative. */ public B capacity(final int capacity) throws IllegalArgumentException { if (0 > capacity) { throw new IllegalArgumentException( "Capacity cannot be negative!"); } this.capacity_ = capacity; return convertThis(); } /** * Load factor is the proportion of the capacity that should be achieved * when evicting. Eviction is trying to reduce the number of elements in * this evictor to capacity times load factor. Load factor must be * between 0 and 1 inclusive! *

* If not called, load factor defaults to {@link #DEFAULT_LOAD_FACTOR}. * * @param loadFactor * The load factor of this evictor. * @return This builder. * @throws IllegalArgumentException * When the argument is not between 0 and 1 inclusive. */ public B loadFactor(final double loadFactor) throws IllegalArgumentException { if (0 > loadFactor || loadFactor > 1) { throw new IllegalArgumentException( "Load factor must be between 0 and 1 inclusive!"); } this.loadFactor_ = loadFactor; return convertThis(); } public Evictor build() { return new RecencyEvictor(capacity_, loadFactor_); } 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, RecencyEvictor.class, 2); final String capacityArg = args[0].trim(); final String loadFactorArg = args[1].trim(); final int capacity = capacityArg.isEmpty() ? DEFAULT_CAPACITY : Integer.valueOf(capacityArg); final double loadFactor = loadFactorArg.isEmpty() ? DEFAULT_LOAD_FACTOR : Double.valueOf(loadFactorArg); return new Builder() .capacity(capacity < 0 ? Integer.MAX_VALUE : capacity) .loadFactor(loadFactor); } @Override public String toString() { return String.format("%s(%d,%f)", RecencyEvictor.class.getName(), capacity_, loadFactor_); } } // Stats. protected class Stats { @Stat public int capacity() { return getCapacity(); } @Stat public int size() { return RecencyEvictor.this.size(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy