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

com.tangosol.util.CircularArrayList Maven / Gradle / Ivy

There is a newer version: 24.09
Show newest version
/*
 * Copyright (c) 2000, 2022, Oracle and/or its affiliates.
 *
 * Licensed under the Universal Permissive License v 1.0 as shown at
 * https://oss.oracle.com/licenses/upl.
 */

package com.tangosol.util;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

import java.util.AbstractList;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.List;
import java.util.RandomAccess;


/**
* Resizable-array implementation of the List interface.  Implements
* all optional list operations, and permits all elements, including
* null. (This class is roughly equivalent to
* ArrayList, except that it is optimized for removing elements at
* the front and back of the list to facilitate use as a queue or deque.

* * The size, isEmpty, get, set, * iterator, and listIterator operations run in constant * time. The add operation runs in amortized constant time, * that is, adding n elements requires O(n) time. All of the other * operations run in linear time (roughly speaking). The constant factor is * low compared to that for the LinkedList implementation.

* * Each CircularArrayList instance has a capacity. * The capacity is the size of the array used to store the elements in the * list. It is always at least as large as the list size. As elements are * added to an CircularArrayList, its capacity grows automatically. The * details of the growth policy are not specified beyond the fact that adding * an element has constant amortized time cost.

* * An application can increase the capacity of an CircularArrayList * instance before adding a large number of elements using the * ensureCapacity operation. This may reduce the amount of * incremental reallocation.

* * Note that this implementation is not synchronized. If * multiple threads access a CircularArrayList concurrently, and at * least one of the threads modifies the list structurally, it must be * synchronized externally. (A structural modification is any operation that * adds or deletes one or more elements, or explicitly resizes the backing * array; merely setting the value of an element is not a structural * modification.) This is typically accomplished by synchronizing on some * object that naturally encapsulates the list. If no object exists, the * list should be "wrapped" using the Collections.synchronizedList * method. This is best done at creation time, to prevent accidental * unsynchronized access to the list: *

*   List list = Collections.synchronizedList(new CircularArrayList(...));
* 

* * The iterators returned by this class's iterator and * listIterator methods are fail-fast: if list is structurally * modified at any time after the iterator is created, in any way except * through the iterator's own remove or add methods, the iterator will throw * a ConcurrentModificationException. Thus, in the face of concurrent * modification, the iterator fails quickly and cleanly, rather than risking * arbitrary, non-deterministic behavior at an undetermined time in the * future.

* * Note that the fail-fast behavior of an iterator cannot be guaranteed as * it is, generally speaking, impossible to make any hard guarantees in the * presence of unsynchronized concurrent modification. Fail-fast iterators * throw ConcurrentModificationException on a best-effort basis. * Therefore, it would be wrong to write a program that depended on this * exception for its correctness: the fail-fast behavior of iterators * should be used only to detect bugs.

* * @author djl 2008.10.22 * * @since Coherence 3.5 */ public class CircularArrayList extends AbstractList implements List, RandomAccess, Cloneable, Serializable { // ----- constructors --------------------------------------------------- /** * Create a new CircularArrayList with default settings. */ public CircularArrayList() { this(16); } /** * Create a new CircularArrayList with the specified initial capacity. * * @param cInitialElements the initial capacity of the list * * @throws IllegalArgumentException if the specified initial capacity * is negative */ public CircularArrayList(int cInitialElements) { if (cInitialElements < 0) { throw new IllegalArgumentException("Illegal Capacity: "+ cInitialElements); } m_aoData = new Object[cInitialElements]; } /** * Construct a CircularArrayList containing the elements of the specified * collection in the order they are returned by the collection's iterator. * * @param c the collection whose elements are to be placed into this list */ public CircularArrayList(Collection c) { m_aoData = new Object[c.size() + 1]; addAll(c); } // ----- CircularArrayList interface ------------------------------------ /** * Trim the capacity of this list instance to be as small as possible. */ public void trimToSize() { modCount++; int cElements = m_cElements; if (cElements + 1 < m_aoData.length) { Object[] aoNewData = new Object[cElements + 1]; toArray(aoNewData); m_aoData = aoNewData; m_iFirst = 0; m_iLast = cElements; } } /** * Increase the capacity of this list instance, if necessary, to ensure * that it can hold at least the specified number of elements. * * @param cMin the minimum allowable capacity * * @return true if the capacity was increased */ public boolean ensureCapacity(int cMin) { int cOld = m_aoData.length; if (cMin > cOld) { int cNew = Math.max(cMin, (cOld * 3) / 2 + 1); m_aoData = toArray(new Object[cNew]); m_iLast = m_cElements; m_iFirst = 0; ++modCount; return true; } return false; } // ----- List interface ------------------------------------------------- /** * {@inheritDoc} */ public int size() { return m_cElements; } /** * {@inheritDoc} */ public boolean isEmpty() { return m_cElements == 0; } /** * {@inheritDoc} */ public boolean contains(Object o) { return indexOf(o) >= 0; } /** * Search for the first occurence of the given argument, testing * for equality using the equals method. * * @param o the element to search for * * @return the index of the first occurrence of the argument in this * list; returns -1 if the object is not found */ public int indexOf(Object o) { Object[] aoData = m_aoData; int cSlots = aoData.length; int iFirst = m_iFirst; for (int i = 0, c = m_cElements; i < c; ++i) { if (Base.equals(o, aoData[(iFirst + i) % cSlots])) { return i; } } return -1; } /** * Returns the index of the last occurrence of the specified object in * this CycicArrayList. * * @param o the element to search for * * @return the index of the last occurrence of the specified object in * this list; returns -1 if the object is not found */ public int lastIndexOf(Object o) { int cElements = m_cElements; int iFirst = m_iFirst; Object[] aoData = m_aoData; int cSlots = aoData.length; for (int i = cElements-1; i >= 0; i--) { if (Base.equals(o, aoData[(i+iFirst) % cSlots])) { return i; } } return -1; } /** * {@inheritDoc} */ public Object[] toArray() { return toArray(new Object[m_cElements]); } /** * {@inheritDoc} */ public Object[] toArray(Object ao[]) { Object[] aoData = m_aoData; int cSlots = aoData.length; int iFirst = m_iFirst; int iLast = m_iLast; int cElements = m_cElements; int co = ao.length; if (co < cElements) { ao = (Object[])java.lang.reflect.Array.newInstance( ao.getClass().getComponentType(), cElements); } else if (co > cElements) { ao[cElements] = null; } if (cElements > 0) { if (iFirst < iLast) { System.arraycopy(aoData, iFirst, ao, 0, cElements); } else { System.arraycopy(aoData, iFirst, ao, 0, cSlots - iFirst); System.arraycopy(aoData, 0, ao, cSlots - iFirst, iLast); } } return ao; } /** * {@inheritDoc} */ public Object get(int index) { return m_aoData[ensureEffectiveIndex(index)]; } /** * {@inheritDoc} */ public Object set(int index, Object o) { Object[] aoData = m_aoData; int iEff = ensureEffectiveIndex(index); Object oOldValue = aoData[iEff]; aoData[iEff] = o; return oOldValue; } /** * {@inheritDoc} */ public boolean add(Object o) { // ensureCapacity may increments modCount ensureCapacity(1 + m_cElements + 1); // must have one extra slot Object[] aoData = m_aoData; int iLast = m_iLast; aoData[iLast] = o; m_iLast = (iLast + 1) % aoData.length; ++m_cElements; return true; } /** * Insert the element at the specified position in this list. Shifts the * element currently at that position (if any) and any subsequent * elements to the right (adds one to their indices). * * @param index the index at which the specified element will be inserted * @param o the element to be inserted * * @throws IndexOutOfBoundsException if index is out of range */ public void add(int index, Object o) { int cElements = m_cElements; if (index == cElements) { // append to tail add(o); return; } rangeCheck(index); ensureCapacity(1 + cElements + 1); // must have one empty slot Object[] aoData = m_aoData; int cSlots = aoData.length; int iFirst = m_iFirst; int iLast = m_iLast; int iEff = effectiveIndex(index); if (iEff == iFirst) { // insert before head m_iFirst = iFirst = (iFirst - 1 + cSlots) % cSlots; aoData[iFirst] = o; } else if (((iFirst > iLast) && (iEff > iFirst)) || iLast == cSlots - 1) { // ... m_iFirst o o o indx o o o m_iLast // o o o m_iLast ... m_iFirst o o o indx o o o // so we have space in front of m_iFirst // shift head to the right System.arraycopy(aoData, iFirst, aoData, iFirst - 1, iEff - iFirst); --m_iFirst; aoData[iEff - 1] = o; ++modCount; } else // iEff < iLast { // m_iFirst o o o indx o o o m_iLast ... // ... m_iFirst o o o indx o o o m_iLast ... // or o o o indx o o o m_iLast ... m_iFirst o o o // shift tail to the left System.arraycopy(aoData, iEff, aoData, iEff + 1, iLast - iEff); m_iLast = (iLast + 1) % cSlots; aoData[iEff] = o; ++modCount; } ++m_cElements; } /** * Removes the element at the specified position in this list. * Shifts any subsequent elements to the left (subtracts one from their * indices). * * @param index the index of the element to removed * * @return the element that was removed from the list * * @throws IndexOutOfBoundsException if index out of range */ public Object remove(int index) { Object[] aoData = m_aoData; int iEff = ensureEffectiveIndex(index); int iFirst = m_iFirst; int iLast = m_iLast; Object oOldValue = aoData[iEff]; // optimized for removing from front. This test and code block // are really the only reason we wrote this class if (iEff == iFirst) { aoData[iEff] = null; m_iFirst = (iFirst + 1) % aoData.length; } else if ((iFirst > iLast) && (iEff > iFirst)) { // o o o m_LastIndex ... m_iFirst o o o iEff o o o // shift head to the right (overwritting iEff) System.arraycopy(aoData, iFirst, aoData, iFirst + 1, iEff - iFirst); aoData[m_iFirst++] = null; } else // iEff < iLast { // ... m_iFirst o o o indx o o o m_iLast // m_iFirst o o o indx o o o m_iLast ... // ... m_iFirst o o o indx o o o m_iLast ... // o o o indx o o o m_iLast ... m_iFirst o o o // shift tail to the left (overitting iEff) System.arraycopy(aoData, iEff + 1, aoData, iEff, iLast - iEff - 1); aoData[--m_iLast] = null; } --m_cElements; ensureCompactness(); ++modCount; return oOldValue; } /** * {@inheritDoc} */ public void clear() { Object[] aoData = m_aoData; int cSlots = aoData.length; int iFirst = m_iFirst; int iLast = m_iLast; m_iFirst = m_iLast = m_cElements = 0; ensureCompactness(); // if ensure didn't create a new array, null out old data if (m_aoData == aoData) { for (int i = iFirst; i != iLast; i = (i + 1) % cSlots) { aoData[i] = null; } } ++modCount; } /** * Appends all of the elements in the specified Collection to the end of * this list, in the order that they are returned by the * specified Collection's Iterator. The behavior of this operation is * undefined if the specified Collection is modified while the operation * is in progress. (This implies that the behavior of this call is * undefined if the specified Collection is this list, and this * list is nonempty.) * * @param c the elements to be inserted into this list * * @return true if this list changed as a result of the call * * @throws NullPointerException if the specified collection is null. */ public boolean addAll(Collection c) { // we could have called addAll(m_cElements, c) // but this is a common case and the size is optimizable friendly int cNew = c.size(); if (cNew == 0) { return false; } // ensureCapacity increments modCount ensureCapacity(1 + m_cElements + cNew); // must have one extra slot Object[] aoData = m_aoData; int cSlots = aoData.length; int iLast = m_iLast; int iNew = 0; Iterator iter = c.iterator(); for (; iNew < cNew && iter.hasNext(); ++iNew) { aoData[iLast] = iter.next(); iLast = (iLast + 1) % cSlots; } m_iLast = iLast; m_cElements += iNew; return true; } /** * Inserts all of the elements in the specified Collection into this * list, starting at the specified position. Shift the element * currently at that position (if any) and any subsequent elements to * the right (increases their indices). The new elements will appear * in the list in the order that they are returned by the * specified Collection's iterator. * * @param index the index at which to insert first element * from the specified collection * @param c the elements to be inserted into this list * * @return true if this list changed as a result of the call * * @throws IndexOutOfBoundsException if index out of range * @throws NullPointerException if the specified Collection is null */ public boolean addAll(int index, Collection c) { int cElements = m_cElements; if (index == cElements) { addAll(c); return true; } rangeCheck(index); int cNew = c.size(); ensureCapacity(1 + cElements + cNew); // must one empty slot Object[] aoData = m_aoData; int cSlots = aoData.length; int iFirst = m_iFirst; int iLast = m_iLast; int iEff = effectiveIndex(index); int iNew = 0; Iterator iter = c.iterator(); if (iEff == iFirst) { // insert at head (no shifting required) m_iFirst = (iFirst - cNew + cSlots) % cSlots; } else if ((iFirst > iLast && iEff > iFirst) || (iFirst < iLast && iFirst - cNew >= 0)) { // o o o m_iLast ... m_iFirst o o o iEff o o o // or ... (lots of room) ... iFirst o o o iLast ... // there is guaranteed room before m_iFirst // shift head to the left System.arraycopy(aoData, iFirst, aoData, iFirst - cNew, iEff - iFirst); m_iFirst = (iFirst - cNew + cSlots) % cSlots; } // ... (too small) ... m_iFirst o o o iEff o o o m_iLast ... // or o o o iEff o o o m_iLast ... m_iFirst o o o else if (iLast + cNew <= cSlots) // || (iFirst > iLast) { // if o o o iEff o o o m_iLast ... m_iFirst o o o // there is guaranteed room after m_iLast // if ... m_iFirst o o o iEff o o o m_iLast ... // room in this case too otherwise test would have failed // shift tail to the right System.arraycopy(aoData, iEff, aoData, iEff + cNew, iLast - iEff); m_iLast = (iLast + cNew) % cSlots; } else // must be ... m_iFirst o o o iEff o o o m_iLast ... { // shoot, we already knew there was no room after iLast, // therefore we must do two moves // shift head to the left; tail to the right System.arraycopy(aoData, iFirst, aoData, 0, iEff - iFirst); int cRight = cNew - iFirst; System.arraycopy(aoData, iEff, aoData, iEff + cRight, iLast - iEff); m_iFirst = 0; m_iLast = iLast + cRight; } iEff = effectiveIndex(index); // m_iFirst might have moved for (; iNew < cNew && iter.hasNext(); ++iNew) { aoData[iEff] = iter.next(); iEff = (iEff + 1) % cSlots; } m_cElements += cNew; ++modCount; if (iNew != cNew) // collection lost elements somehow { removeRange(index + iNew, index + cNew + 1); } return true; } /** * Removes from this list all of the elements whose indexes are * between fromIndex, inclusive and toIndex, exclusive. Shifts any * succeeding elements to the left (reduces their index). * This call shortens the list by (toIndex - fromIndex) elements. * (If toIndex==fromIndex, this operation has no effect.) * * @param fromIndex the index of first element to be removed * @param toIndex the index after last element to be removed. */ protected void removeRange(int fromIndex, int toIndex) { rangeCheck(fromIndex); rangeCheck(toIndex - 1); if (fromIndex >= toIndex) { return; } Object[] aoData = m_aoData; int cSlots = aoData.length; int iFrom = effectiveIndex(fromIndex); int iTo = effectiveIndex(toIndex); int iFirst = m_iFirst; int iLast = m_iLast; int cRemoved = iTo - iFrom; if (iFirst < iLast || (iFrom < iLast && iTo < iLast)) { // ... iFirst o o o [from o o o to) o o o iLast ... // or // o o o [from o o o to) o o o iLast ... iFirst o o o int cMoved = iLast - iTo; int iNewLast = iLast - cRemoved; System.arraycopy(aoData, iTo, aoData, iFrom, cMoved); while (iLast != iNewLast) { aoData[--iLast] = null; } m_iLast = iLast; } else if (iFrom >= iFirst && iTo > iFirst) { // o o o iLast ... iFirst o o o [from o o o to) o o o int cMoved = iFrom - iFirst; int iNewFirst = iTo - cMoved; System.arraycopy(aoData, iFirst, aoData, iNewFirst, cMoved); while (iFirst != iNewFirst) { aoData[iFirst++] = null; } m_iFirst = iFirst; } else // if (iFrom >= iFirst && iTo <= iLast) { // o o o to) o o o iLast ... iFirst o o o [from o o o // shift head to the right int cMovedEnd = iFrom - iFirst; int iNewFirst = cSlots - cMovedEnd; if (cMovedEnd > 0) { System.arraycopy(aoData, iFirst, aoData, iNewFirst, cMovedEnd); } while (iFirst != iNewFirst) { aoData[iFirst++] = null; } // iFrom could have been == iFirst m_iFirst = iNewFirst % cSlots; if (iTo != 0) { // shift tail to the left int iNewLast = iLast - iTo; System.arraycopy(aoData, iTo, aoData, 0, iNewLast); while (iLast != iNewLast) { aoData[--iLast] = null; } m_iLast = iLast; } } m_cElements -= cRemoved; ensureCompactness(); ++modCount; } // ----- Serializable Interface ----------------------------------------- /** * Save the state of the list instance to a stream (that * is, serialize it). * * @param s The stream to write to * * @throws IOException */ private void writeObject(ObjectOutputStream s) throws IOException { int preMod = modCount; int iFirst = m_iFirst; int iLast = m_iLast; Object[] aoData = m_aoData; int cSlots = aoData.length; s.writeInt(m_cElements); for (int i = iFirst; i != iLast; i = (i+1) % cSlots) { s.writeObject(aoData[i]); } if (preMod != modCount) { throw new ConcurrentModificationException(); } } /** * Reconstitute the list instance from a stream (that is, * deserialize it). * * @param s The stream to read from * * @throws IOException if an I/O error occurs reading this object * @throws ClassNotFoundException if the class for an object being * read cannot be found */ private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { Object[] aoData = readObjectArray(s); m_iFirst = 0; m_iLast = aoData.length; m_cElements = aoData.length - 1; m_aoData = aoData; } // ----- Clonable Interface -------------------------------------------- /** * Returns a shallow copy of this list instance. The * elements themselves are not copied. * * @return a clone of this list instance */ public Object clone() { try { int preMod = modCount; CircularArrayList cal = (CircularArrayList) super.clone(); cal.m_aoData = (Object[]) m_aoData.clone(); if (preMod != modCount) { throw new ConcurrentModificationException(); } return cal; } catch (CloneNotSupportedException e) { // this shouldn't happen, since we are Cloneable throw Base.ensureRuntimeException(e); } } // ----- helpers -------------------------------------------------------- /** * Read an array of objects from a DataInput stream. * * @param in a ObjectInputStream stream to read from * * @return an array of objects * * @throws IOException if an I/O exception occurs * * @since 22.09 */ private static Object[] readObjectArray(ObjectInputStream in) throws IOException, ClassNotFoundException { int c = in.readInt(); // JEP-290 - ensure we can allocate this array ExternalizableHelper.validateLoadArray(Object[].class, c, in); return c <= 0 ? new Object[0] : c < ExternalizableHelper.CHUNK_THRESHOLD >> 4 ? readObjectArray(in, c, true) : readLargeObjectArray(in, c); } /** * Read an array of the specified number of objects from a DataInput stream. * * @param in a DataInput stream to read from * @param cLength length to read * @param fPad if the returned array should be padded by one * * @return an array of objects * * @throws IOException if an I/O exception occurs * * @since 22.09 */ private static Object[] readObjectArray(ObjectInputStream in, int cLength, boolean fPad) throws IOException, ClassNotFoundException { Object[] ao = new Object[cLength + (fPad ? 1 : 0)]; for (int i = 0; i < cLength; i++) { ao[i] = in.readObject(); } return ao; } /** * Read an array of objects with length larger than {@link ExternalizableHelper#CHUNK_THRESHOLD} {@literal >>} 4. * * @param in a DataInput stream to read from * @param cLength length to read * * @return an array of objects * * @throws IOException if an I/O exception occurs * * @since 22.09 */ private static Object[] readLargeObjectArray(ObjectInputStream in, int cLength) throws IOException, ClassNotFoundException { int cBatchMax = ExternalizableHelper.CHUNK_SIZE >> 4; int cBatch = cLength / cBatchMax + 1; Object[] aMerged = null; int cRead = 0; int cAllocate = cBatchMax; Object[] ao; for (int i = 0; i < cBatch && cRead < cLength; i++) { ao = readObjectArray(in, cAllocate, cAllocate < cBatchMax); aMerged = ExternalizableHelper.mergeArray(aMerged, ao); cRead += ao.length; cAllocate = Math.min(cLength - cRead, cBatchMax); } return aMerged; } /** * Calculate the effective index taking into account offsets and the * circular nature of CircularArrayList. * * @param index the index to transform * * @return the effective index in the physical storage array */ protected int effectiveIndex(int index) { return (m_iFirst + index ) % m_aoData.length; } /** * Check if the given index is in range. If not, throw an appropriate * runtime exception. * * @param index the index to be checked for being between * size() and 0 inclusive * * @throws IndexOutOfBoundsException if the index is not in the range */ protected void rangeCheck(int index) { if (index >= m_cElements || index < 0) { throw new IndexOutOfBoundsException( "Index: " + index + ", Size: " + m_cElements); } } /** * After range checking Calculate the effective index while taking into * account the offsets and the circular nature of the list. * * @param index the index to transform * * @return the effective index in the physical storage array * @throws IndexOutOfBoundsException if the index is not in the range */ protected int ensureEffectiveIndex(int index) { if (index >= m_cElements || index < 0) { throw new IndexOutOfBoundsException( "Index: " + index + ", Size: " + m_cElements); } return (m_iFirst + index) % m_aoData.length; } /** * Ensure the representation of this list is appropriatly compact * by shrinking if necessary. * * @return true if an actual compaction happened; false otherwise */ protected boolean ensureCompactness() { return false; } /** * Outputs information to standard output about representation * for debugging purposes. */ public void dump() { System.out.println ("\niFirst = " + m_iFirst + " iLast= " + m_iLast + " count= " + m_cElements); for (int i = 0; i < m_aoData.length; i++) { System.out.print(m_aoData[i]); if (i != m_aoData.length - 1) { System.out.print(", "); } } System.out.println(); } // ----- data members ---------------------------------------------------- /** * The array into which the elements of the list are stored. * The capacity of the list is the length of this array buffer. */ protected Object[] m_aoData = null; /** * The offset to the first element. */ protected int m_iFirst = 0; /** * The offset to one past the last element. */ protected int m_iLast = 0; /** * The size of the list (the number of elements it contains). */ protected int m_cElements = 0; }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy