/******************************************************************************* * ALMA - Atacama Large Millimeter Array * Copyright (c) ESO - European Southern Observatory, 2011 * (in the framework of the ALMA collaboration). * All rights reserved. * * This 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *******************************************************************************/ /** * */ package com.cosylab.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.Iterator; import java.util.List; /** * Cicruclar array list implementation. * @author msekoranja */ public class CircularArrayList<T> extends AbstractList<T> implements List<T>, Serializable { private static final long serialVersionUID = -1794729598452372631L; // array of data private T[] elementData; // head points to the first logical element in the array, and // tail points to the element following the last. This means // that the list is empty when head == tail. It also means // that the elementData array has to have an extra space in it. private int head = 0, tail = 0; // Strictly speaking, we don't need to keep a handle to size, // as it can be calculated programmatically, but keeping it // makes the algorithms faster. private int size = 0; public CircularArrayList() { this(10); } @SuppressWarnings("unchecked") public CircularArrayList(int size) { elementData = (T[]) new Object[size]; } @SuppressWarnings("unchecked") public CircularArrayList(Collection<T> c) { tail = c.size(); elementData = (T[]) new Object[c.size()]; c.toArray(elementData); } // The convert() method takes a logical index (as if head was // always 0) and calculates the index within elementData private int convert(int index) { return (index + head) % elementData.length; } public boolean isEmpty() { return head == tail; // or size == 0 } // We use this method to ensure that the capacity of the // list will suffice for the number of elements we want to // insert. If it is too small, we make a new, bigger array // and copy the old elements in. @SuppressWarnings("unchecked") public void ensureCapacity(int minCapacity) { int oldCapacity = elementData.length; if (minCapacity > oldCapacity) { int newCapacity = (oldCapacity * 3) / 2 + 1; if (newCapacity < minCapacity) newCapacity = minCapacity; T newData[] = (T[]) new Object[newCapacity]; toArray(newData); tail = size; head = 0; elementData = newData; } } public int size() { // the size can also be worked out each time as: // (tail + elementData.length - head) % elementData.length return size; } public boolean contains(Object elem) { return indexOf(elem) >= 0; } public int indexOf(Object elem) { if (elem == null) { for (int i = 0; i < size; i++) if (elementData[convert(i)] == null) return i; } else { for (int i = 0; i < size; i++) if (elem.equals(elementData[convert(i)])) return i; } return -1; } public int lastIndexOf(Object elem) { if (elem == null) { for (int i = size - 1; i >= 0; i--) if (elementData[convert(i)] == null) return i; } else { for (int i = size - 1; i >= 0; i--) if (elem.equals(elementData[convert(i)])) return i; } return -1; } public Object[] toArray() { Object[] result = new Object[size]; System.arraycopy(elementData, 0, result, 0, size); return result; } @SuppressWarnings({ "unchecked", "hiding" }) public <T> T[] toArray(T a[]) { if (a.length < size) a = (T[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size); if (head < tail) { System.arraycopy(elementData, head, a, 0, tail - head); } else { System.arraycopy(elementData, head, a, 0, elementData.length - head); System.arraycopy(elementData, 0, a, elementData.length - head, tail); } if (a.length > size) a[size] = null; return a; } private void rangeCheck(int index) { if (index >= size || index < 0) throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size); } public T get(int index) { rangeCheck(index); return elementData[convert(index)]; } public T set(int index, T element) { modCount++; rangeCheck(index); T oldValue = elementData[convert(index)]; elementData[convert(index)] = element; return oldValue; } public boolean add(T o) { modCount++; // We have to have at least one empty space ensureCapacity(size + 1 + 1); elementData[tail] = o; tail = (tail + 1) % elementData.length; size++; return true; } // This method is the main reason we re-wrote the class. // It is optimized for removing first and last elements // but also allows you to remove in the middle of the list. public T remove(int index) { modCount++; rangeCheck(index); int pos = convert(index); // an interesting application of try/finally is to avoid // having to use local variables try { return elementData[pos]; } finally { elementData[pos] = null; // Let gc do its work // optimized for FIFO access, i.e. adding to back and // removing from front if (pos == head) { head = (head + 1) % elementData.length; } else if (pos == tail) { tail = (tail - 1 + elementData.length) % elementData.length; } else { if (pos > head && pos > tail) { // tail/head/pos System.arraycopy(elementData, head, elementData, head + 1, pos - head); head = (head + 1) % elementData.length; } else { System.arraycopy(elementData, pos + 1, elementData, pos, tail - pos - 1); tail = (tail - 1 + elementData.length) % elementData.length; } } size--; } } public void clear() { modCount++; // Let gc do its work for (int i = head; i != tail; i = (i + 1) % elementData.length) elementData[i] = null; head = tail = size = 0; } public boolean addAll(Collection<? extends T> c) { modCount++; int numNew = c.size(); // We have to have at least one empty space ensureCapacity(size + numNew + 1); Iterator<? extends T> e = c.iterator(); for (int i = 0; i < numNew; i++) { elementData[tail] = e.next(); tail = (tail + 1) % elementData.length; size++; } return numNew != 0; } public void add(int index, T element) { throw new UnsupportedOperationException(); } public boolean addAll(int index, Collection<? extends T> c) { throw new UnsupportedOperationException(); } private synchronized void writeObject(ObjectOutputStream s) throws IOException { s.writeInt(size); for (int i = head; i != tail; i = (i + 1) % elementData.length) s.writeObject(elementData[i]); } @SuppressWarnings("unchecked") private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { // Read in size of list and allocate array head = 0; size = tail = s.readInt(); elementData = (T[]) new Object[tail]; // Read in all elements in the proper order. for (int i = 0; i < tail; i++) elementData[i] = (T)s.readObject(); } }