/** Copyright 2015 Tim Engler, Rareventure LLC This file is part of Tiny Travel Tracker. Tiny Travel Tracker is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Tiny Travel Tracker 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Tiny Travel Tracker. If not, see <http://www.gnu.org/licenses/>. */ package com.rareventure.util; import java.util.AbstractList; import java.util.Comparator; import com.rareventure.gps2.database.TAssert; public class CircularList<T> extends AbstractList<T> { private T[] items; private int maxSize; private int size = 0; private int start; public CircularList(int maxSize) { items = (T[]) new Object[maxSize]; this.maxSize = maxSize; } @Override public T get(int index) { if (index >= size) throw new ArrayIndexOutOfBoundsException("Yo!"); return items[(start + index) % maxSize]; } @Override public int size() { return size; } @Override public void clear() { start = 0; size = 0; } /** * Adds an item to the queue, which if at max size will remove the last item and return it * @param item * @return */ public T addNoExpand(T item) { if (size < maxSize) { items[(start + size) % maxSize] = item; size++; return null; } else { T removedItem = items[start]; items[start] = item; start = (start + 1) % maxSize; return removedItem; } } /** * * @return first item in queue, removing it */ public T shift() { if(size == 0) throw new IllegalStateException("shift when zero size"); T removedItem = items[start]; start = (start + 1) % maxSize; size --; return removedItem; } @Override public boolean isEmpty() { return size == 0; } @Override public boolean add(T item) { if (size >= maxSize) { growForInsert(1); } items[(start + size) % maxSize] = item; size++; return true; } private void growForInsert(int required) { int increment = size / 2; if (required > increment) { increment = required; } if (increment < 12) { increment = 12; } T[] newArray = (T[]) new Object[size + increment]; System.arraycopy(items, start, newArray, 0, items.length - start); if(start != 0) System.arraycopy(items, 0, newArray, items.length-start, start); start = 0; maxSize = newArray.length; items = newArray; } public int binarySearch(T key, Comparator<T> c) { int low = 0; int high = size - 1; while (low <= high) { int mid = (low + high) >>> 1; T midVal = items[(mid + start) % maxSize]; int cmp = c.compare(midVal, key); if (cmp < 0) low = mid + 1; else if (cmp > 0) high = mid - 1; else return mid; // key found } return -(low + 1); // key not found. } /** * pushes the last element in the circular list to the first */ public void moveLastToFirst() { if (maxSize == size) { start = (start - 1 + maxSize) % maxSize; } else { throw new IllegalStateException( "not implemented unless at max size"); } } public T getLast() { if(size == 0) TAssert.fail("get last with size 0"); return items[size == maxSize ? (start + maxSize - 1) % maxSize : size - 1]; } public void replaceLast(T item) { items[size == maxSize ? (start + maxSize - 1) % maxSize : size - 1] = item; } }