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

net.sf.eBus.util.IndexPool Maven / Gradle / Ivy

//
// Copyright 2001 - 2005, 2011, 2013 - 2014 Charles W. Rapp
//
// 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.
//

package net.sf.eBus.util;

import java.io.Serializable;
import java.util.Iterator;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * {@code IndexPool} provides integer index value reuse. When
 * you create an {@code IndexPool}, you may specify the minimum
 * and maximum allowed values (inclusive). You retrieve the next
 * available value using {@link #nextIndex} and return values to
 * the pool with {@link #returnIndex}. When the pool is
 * exhausted, {@link #nextIndex} throws an
 * {@code IllegalStateException}. The application is responsible
 * for returning unused indices to the pool by calling
 * {@link #returnIndex(int)}. The application is also responsible
 * for making sure that {@link #returnIndex(int)} calls contain
 * unique indicies and that the same index does not appear in the
 * pool multiple times.
 * 

* {@code IndexPool} uses both an {@link AtomicInteger} and a * {@link ConcurrentLinkedDeque} (as a stack) to track available * indices. When {@link #nextIndex()} is called, it first checks * if the index stack is empty. If it is empty, then * {@link AtomicInteger#getAndIncrement()} is called to get the * next unused index. If the returned value is > than the * maximum allowed index, then {@link IllegalStateException} * exception is thrown. If the index stack is not empty, then * the an index is popped off the stack and returned. The idea * behind this technique is to continually use the same indices * in the hopes this improves processor cache hits. *

*

* {@code IndexPool} has no memory between application * executions. If you need to persist index values between * executions, use {@link net.sf.eBus.util.IndexCache}. *

*

* Note: {@code IndexPool} is * synchronized. It is safe for multiple threads to access the * same {@code IndexPoool} instance without synchronizing. *

* * @see net.sf.eBus.util.IndexCache * * @author Charles Rapp */ public final class IndexPool implements Serializable { //--------------------------------------------------------------- // Member data. // //----------------------------------------------------------- // Constants. // /** * This is eBus version 2.1.0. */ private static final long serialVersionUID = 0x050200L; //----------------------------------------------------------- // Locals. // /** * The next index value to be returned. */ private AtomicInteger mNextIndex; /** * The minimum index value which may be used. */ private final int mMinIndex; /** * The maximum index value which may be used (inclusive). */ private final int mMaxIndex; /** * Store returned indices here as a stack. The most recently * returned index is the next available index. */ private ConcurrentLinkedDeque mPool; //--------------------------------------------------------------- // Member methods. // //----------------------------------------------------------- // Constructors. // /** * Creates a pool with the default size. *

* The default settings are: *

    *
  • * Minimum index is zero (this will be the * first assigned value). *
  • *
  • * Maximum index is {@code Integer.MAX_VALUE}. *
  • *
*/ public IndexPool() { this (0, Integer.MAX_VALUE); } // end of IndexPool() /** * Creates a pool with the specified minimum and maximum * indicies (inclusive). * @param minIndex the minimum index ({@link #nextIndex} will * return this value first.) * @param maxIndex the maximum index (inclusive). * @throws IllegalArgumentException * if: *
    *
  • * {@code minIndex} is <= zero. *
  • *
  • * {@code maxIndex} is <= zero. *
  • *
  • * {@code minIndex} is > {@code maxIndex}. *
  • *
*/ public IndexPool(final int minIndex, final int maxIndex) { if (minIndex < 0) { throw ( new IllegalArgumentException( "minIndex < 0 (" + Integer.toString(minIndex) + ")")); } else if (maxIndex < 0) { throw ( new IllegalArgumentException( "maxIndex < 0 (" + Integer.toString(maxIndex) + ")")); } else if (minIndex > maxIndex) { throw ( new IllegalArgumentException( "minIndex (" + Integer.toString(minIndex) + " > maxIndex (" + Integer.toString(maxIndex) + ")")); } mNextIndex = new AtomicInteger(minIndex); mMinIndex = minIndex; mMaxIndex = maxIndex; mPool = new ConcurrentLinkedDeque<>(); } // end of IndexPool(int, int) // // end of Constructors. //----------------------------------------------------------- //----------------------------------------------------------- // Object Method Overrides. // /** * Returns a textual representation of this index pool. * @return a textual representation of this index pool. */ @Override public String toString() { final Iterator pit; String separator = ""; final StringBuilder retval = new StringBuilder(); retval.append("minimum index = ").append(mMinIndex) .append("\nmaximum index = ").append(mMaxIndex) .append("\ncached indices = {"); for (pit = mPool.iterator(); pit.hasNext(); separator = ", ") { retval.append(separator).append(pit.next()); } retval.append("}\nnext index = ") .append(mNextIndex.get()); return (retval.toString()); } // end of toString() // // end of Object Method Overrides. //----------------------------------------------------------- //----------------------------------------------------------- // Get methods. // /** * Returns the pool's minimum index. * @return the pool's minimum index. */ public int minIndex() { return (mMinIndex); } // end of minIndex() /** * Returns the pool's maximum index. * @return the pool's maximum index. */ public int maxIndex() { return (mMaxIndex); } // end of maxIndex() // // end of Get methods. //----------------------------------------------------------- /** * Returns the next available index. * @return the next available index. * @exception IllegalStateException * if all indices are in use and the pool is exhausted. This * may be due to a failure to return unused indicies to the * pool. */ public int nextIndex() { Integer retval = mPool.pollFirst(); if (retval == null) { // Generate the next value. retval = mNextIndex.getAndIncrement(); // Is this pool exhausted? if (retval> mMaxIndex) { // Yes, it is very tired and needs rest. throw ( new IllegalStateException( "index pool is exhausted")); } } return (retval); } // end of nextIndex() /** * Puts an index back into the pool for reuse. *

* Note: this method does not prevent * returning the same index to the pool multiple times. It * is the caller's responsibility to make sure that an index * appears only once in the pool. *

* @param index Put this index back in the pool. * @exception IndexOutOfBoundsException * if {@code index} is less than the minimum index value * or greater than or equal to the maximum index value. */ public void returnIndex(final int index) { if (index < mMinIndex) { throw ( new IndexOutOfBoundsException( Integer.toString(index) + " < minimum index" + Integer.toString(mMinIndex))); } else if (index > mMaxIndex) { throw ( new IndexOutOfBoundsException( Integer.toString(index) + " > maximum index" + Integer.toString(mMaxIndex))); } else { // Put this index into the pool. mPool.add(index); } } // end of returnIndex(int) /** * Resets the index pool to its initial state. All indices * are available for use. Any outstanding indices are * invalid. */ public void reset() { mNextIndex.set(mMinIndex); mPool.clear(); } // end of reset() } // end of IndexPool




© 2015 - 2025 Weber Informatics LLC | Privacy Policy