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

org.divxdede.collection.CyclicBuffer Maven / Gradle / Ivy

/*
 * Copyright (c) 2010 INFASS Syst?mes (http://www.infass.com) All rights reserved.
 * CyclicBuffer.java is a part of this Commons library
 * ====================================================================
 *
 * Commons library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of the License,
 * or any later version.
 *
 * This is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see .
 */
package org.divxdede.collection;

import java.util.AbstractCollection;
import java.util.Arrays;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Queue;

/**
 * Cyclic buffer implementing the {@link Queue} interface.
 * 

* This buffer has a fixed size ({@link #getCapacity()}, {@link #setCapacity(int)}.
* When a object is inserted inside this buffer, if the buffer is out of capacity (too small for receive the new object), * the last object inserted inide this buffer will be removed for let the place to the new one.
* The fact to privilegiate newers insertion over oldest make this buffer a cyclic buffer. *

* This buffer don't implements {@link java.util.List} collection but own somme confortable methods like: *

    *
  • {@link #get(int)}
  • *
  • {@link #set(int, java.lang.Object)}
  • *
*

* Methods from the {@link Queue} interface are thread-safe but others are not. * Methods from the {@link Collection} interface are not thread safe. * * @author Andr? S?bastien (divxdede) */ public class CyclicBuffer extends AbstractCollection implements Queue { private Object[] array = null; private int start = 0; // included index private int end = 0; // excluded index private int count = 0; private int modCount = 0; /** Create a default cyclic buffer with a capacity of 10 */ public CyclicBuffer() { this(10); } /** Creates a new instance of CyclicBuffer */ public CyclicBuffer(int capacity) { setCapacity(capacity); } /* Returns the number of elements in this collection. If this collection * contains more than Integer.MAX_VALUE elements, returns * Integer.MAX_VALUE. * * @return the number of elements in this collection */ public int size() { return count; } /** * Returns an iterator over the elements in this collection. * Older for most recent order * * @return an Iterator over the elements in this collection */ public Iterator iterator() { return new Itr(); } /** * Returns an array containing all of the elements in this collection. If * the collection makes any guarantees as to what order its elements are * returned by its iterator, this method must return the elements in the * same order.

* * The returned array will be "safe" in that no references to it are * maintained by this collection. (In other words, this method must * allocate a new array even if this collection is backed by an array). * The caller is thus free to modify the returned array.

* * This method acts as bridge between array-based and collection-based * APIs. * * @return an array containing all of the elements in this collection */ public Object[] toArray() { Object[] a = new Object[ size() ]; if( a.length == 0 ) return a; return toArrayImpl(a); } /** * Returns an array containing all of the elements in this collection; * the runtime type of the returned array is that of the specified array. * If the collection fits in the specified array, it is returned therein. * Otherwise, a new array is allocated with the runtime type of the * specified array and the size of this collection.

* * If the collection fits in the specified array with room to spare (i.e., * the array has more elements than the collection), the element in the * array immediately following the end of the collection is set to * null. This is useful in determining the length of the * collection only if the caller knows that the collection does * not contain any null elements.)

* * If this collection makes any guarantees as to what order its elements * are returned by its iterator, this method must return the elements in * the same order.

* * If the array isn't big-enough, only most-recents elements on the array * * @param a the array into which the elements of the collection are to * be stored, if it is big enough; otherwise, a new array of the * same runtime type is allocated for this purpose. * @return an array containing the elements of the collection. * * @throws NullPointerException if the specified array is null. * * @throws ArrayStoreException if the runtime type of the specified array * is not a supertype of the runtime type of every element in this * collection. */ public T[] toArray(T[] a) { return (T[])toArrayImpl( a); } /** * Ensures that this collection contains the specified element (optional * operation). Returns true if the collection changed as a * result of the call. (Returns false if this collection does * not permit duplicates and already contains the specified element.) * Collections that support this operation may place limitations on what * elements may be added to the collection. In particular, some * collections will refuse to add null elements, and others will * impose restrictions on the type of elements that may be added. * Collection classes should clearly specify in their documentation any * restrictions on what elements may be added.

* * This implementation always throws an * UnsupportedOperationException. * * @param o element whose presence in this collection is to be ensured. * @return true if the collection changed as a result of the call. * * @throws UnsupportedOperationException if the add method is not * supported by this collection. * * @throws NullPointerException if this collection does not permit * null elements, and the specified element is * null. * * @throws ClassCastException if the class of the specified element * prevents it from being added to this collection. * * @throws IllegalArgumentException if some aspect of this element * prevents it from being added to this collection. */ public boolean add(E o) { if( end == start && !isEmpty() ) { // We will override the oldest entry by the new one start++; if( start == getCapacity() ) start = 0; } // set the new entry array[end++] = o; if( end == getCapacity() ) end = 0; // update the size() of this collection count = Math.min( count + 1 , getCapacity() ); // return the success modCount++; return true; } /** Retrieve a element from it's index inside this buffer. *

* Be careful, this buffer is cyclic, that mean that an object can be moved inside this buffer.
* This method is not thread-safe, and should be used carefully */ public E get(int index) { if( index < 0 || index >= size() ) throw new IllegalArgumentException("index " + index + " out of bound"); return (E)array[ logicalIndexToPhysicalIndex(index) ]; } /** Returns the last inserted item inside this cyclic buffer. * @return The last inserted item inside this cyclic buffer. * @see #getFirst() * @since 0.2 */ public E getLast() { if( isEmpty() ) return null; return get( size() - 1 ); } /** Returns the oldest inserted item inside this cyclic buffer that was not yet evicted. * @return The oldest inserted item inside this cyclic buffer that was not yet evicted. * @see #getLast() * @since 0.2 */ public E getFirst() { if( isEmpty() ) return null; return get(0); } /** Set an object at a specified index * Be careful, this buffer is cyclic, that mean that an object can be moved inside this buffer.
* This method is not thread-safe, and should be used carefully */ public void set(int index , E object ) { if( index < 0 || index >= size() ) throw new IllegalArgumentException("index " + index + " out of bound"); array[ logicalIndexToPhysicalIndex(index) ] = object; } /** Return the physical index inside the backed array for the first element of this buffer */ private int getFirstElementPhysicalIndex() { return start; } /** Convert a logical index (index in the buffer coordinate) into a physical index (index in the backed array coordinate) */ private int logicalIndexToPhysicalIndex( int logicalIndex ) { return ( ( getFirstElementPhysicalIndex() + logicalIndex ) % getCapacity() ); } /** Unsupported operation */ public boolean remove(Object o) { throw new UnsupportedOperationException("remove(Object) is unsupported by CyclicBuffer, use peek() or remove() for removing last entry"); } /** Unsupported operation */ public boolean removeAll(Collection c) { throw new UnsupportedOperationException("removeAll(Collection) is unsupported by CyclicBuffer"); } /** Unsupported operation */ public boolean retainAll(Collection c) { throw new UnsupportedOperationException("retainAll(Collection) is unsupported by CyclicBuffer"); } /** * Removes all of the elements from this collection (optional operation). * The collection will be empty after this call returns (unless it throws * an exception).

* * This implementation iterates over this collection, removing each * element using the Iterator.remove operation. Most * implementations will probably choose to override this method for * efficiency.

* * Note that this implementation will throw an * UnsupportedOperationException if the iterator returned by this * collection's iterator method does not implement the * remove method and this collection is non-empty. * * @throws UnsupportedOperationException if the clear method is * not supported by this collection. */ public void clear() { start = 0; end = 0; count = 0; Arrays.fill( array , 0 , array.length , null ); modCount++; } /** * Inserts the specified element into this buffer. * If the buffer is full, the oldest-entry is removed for allowing this insertion. * * @param o the element to insert. * @return true */ public synchronized boolean offer(E o) { return add(o); } /** * Retrieves and removes the head of this queue, or null * if this queue is empty. * * @return the head of this queue, or null if this * queue is empty. */ public synchronized E poll() { if( isEmpty() ) return null; // give result E result = getLast(); // remove end--; count--; if( end < 0 ) end = getCapacity() - 1; modCount++; return result; } /** * Retrieves and removes the head of this queue. This method * differs from the poll method in that it throws an * exception if this queue is empty. * * @return the head of this queue. * @throws NoSuchElementException if this queue is empty. */ public synchronized E remove() { if( isEmpty() ) throw new NoSuchElementException(); return poll(); } /** * Retrieves, but does not remove, the head of this queue, * returning null if this queue is empty. * * @return the head of this queue, or null if this queue * is empty. */ public synchronized E peek() { return getLast(); } /** * Retrieves, but does not remove, the head of this queue. This method * differs from the peek method only in that it throws an * exception if this queue is empty. * * @return the head of this queue. * @throws NoSuchElementException if this queue is empty. */ public synchronized E element() { if( isEmpty() ) throw new NoSuchElementException(); return peek(); } /** Define the new capacity of this cyclic buffer. * If the new capacity is less than current, only most-recent elements are preserved to fit the new capacity. * @param new capacity, must be > 0 */ public void setCapacity(int newCapacity) { if( newCapacity <= 0 ) throw new IllegalArgumentException("capacity can't be less than 1"); int oldSize = size(); this.array = toArrayImpl( new Object[newCapacity] ); this.start = 0; this.end = oldSize > newCapacity ? 0 : oldSize; this.count = oldSize > newCapacity ? newCapacity : oldSize; modCount++; } /** Retrieve the capacity of this cyclic buffer */ public int getCapacity() { return this.array == null ? 0 : this.array.length; } /** Internal method for populate a new array with datas from this buffer */ private Object[] toArrayImpl( Object[] result ) { int newSize = result.length; if( newSize <= 0) throw new IllegalArgumentException("size can't be less than 1"); if( array != null ) { int copied = count > newSize ? newSize : count; int skipped = count - copied; int effectiveStart = start; effectiveStart += skipped; if( effectiveStart >= getCapacity() ) { effectiveStart = effectiveStart - getCapacity(); } if( end > effectiveStart ) { System.arraycopy( array , effectiveStart , result , 0 , end - effectiveStart ); } else { System.arraycopy( array , effectiveStart , result , 0 , getCapacity() - effectiveStart ); System.arraycopy( array , 0 , result , getCapacity() - effectiveStart , end ); } } return result; } /** Internal iterator */ private class Itr implements Iterator { int shift = start; int current = 0; int expectedModCount = modCount; public boolean hasNext() { checkForComodification(); return current < size(); } public E next() { if( ! hasNext() ) throw new NoSuchElementException(); try { int index = current + shift; if( index >= getCapacity() ) index = index - getCapacity(); Object result = array[index]; current++; return (E)result; } catch(Exception e) { e.printStackTrace() ; checkForComodification(); } return null; } public void remove() { throw new UnsupportedOperationException(); } private void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy