001package org.javasimon.callback.lastsplits; 002 003import java.util.AbstractList; 004import java.util.Arrays; 005import java.util.Collection; 006import java.util.Iterator; 007 008/** 009 * Ring/circular buffer, fixed size FIFO list. Stores values as a fixed size 010 * array looping insertion point when array is full. Warnings: <ul> <li>Only 011 * needed methods of the List interface have been implemented.</li> <li>This 012 * class is not thread safe</li> </ul> 013 * 014 * @author gquintana 015 * @since 3.2 016 */ 017public class CircularList<T> extends AbstractList<T> { 018 019 /** Elements. */ 020 private final T[] elements; 021 /** 022 * Index + 1 of the last element. 023 * Or said differently index where will be added the next element. 024 */ 025 private int lastIndex; 026 /** 027 * Index of the the first element. 028 * Or said differently index which will be removed when list capacity is achieved. 029 */ 030 private int firstIndex = -1; 031 /** Number of element stored in this list. */ 032 private int size; 033 034 /** 035 * Constructor. 036 * 037 * @param capacity Size of the ring buffer 038 */ 039 public CircularList(int capacity) { 040 elements = (T[]) new Object[capacity]; 041 } 042 043 /** 044 * Get capacity (buffer size). 045 * 046 * @return capacity 047 */ 048 public int getCapacity() { 049 return elements.length; 050 } 051 052 /** 053 * Converts an index starting from 0 into an index starting from first 054 * 055 * @param index Index 056 * @return Index converted 057 */ 058 private int convertIndex(int index) { 059 return (lastIndex + index) % elements.length; 060 } 061 062 /** 063 * Get element by index 064 * 065 * @param index Index 066 * @return Element 067 */ 068 public T get(int index) { 069 return elements[convertIndex(index)]; 070 } 071 072 /** 073 * Return the number of elements in the list 074 * 075 * @return Size 076 */ 077 public int size() { 078 return size; 079 } 080 081 /** 082 * Tells whether the list is empty 083 * 084 * @return true if empty 085 */ 086 @Override 087 public boolean isEmpty() { 088 return size == 0; 089 } 090 091 /** 092 * Increment an index 093 * 094 * @param index Input index 095 * @param maxIndex Assigned value when capacity is reached 096 * @return Output index 097 */ 098 private int incrementIndex(int index, int maxIndex) { 099 int newIndex = index + 1; 100 if (newIndex >= elements.length) { 101 newIndex = maxIndex; 102 } 103 return newIndex; 104 } 105 106 /** 107 * Add an element to the list, overwriting last added element when 108 * list's capacity is reached 109 * 110 * @param newElement Element to add 111 * @return Always true 112 */ 113 @Override 114 public boolean add(T newElement) { 115 elements[lastIndex] = newElement; 116 lastIndex = incrementIndex(lastIndex, 0); 117 if (isEmpty()) { 118 // First element 119 firstIndex = 0; 120 size = 1; 121 } else if (isFull()) { 122 // Reuse space 123 firstIndex = lastIndex; 124 } else { 125 size = incrementIndex(size, elements.length); 126 } 127 return true; 128 } 129 130 private boolean isFull() { 131 return size == elements.length; 132 } 133 134 /** 135 * Inserts a collection of elements, looping on them. 136 * 137 * @param newElements Collection of elements to add 138 * @return Always true 139 */ 140 @Override 141 public boolean addAll(Collection<? extends T> newElements) { 142 for (T newElement : newElements) { 143 add(newElement); 144 } 145 return true; 146 } 147 148 /** 149 * Removes the first (inserted) element of the collection. 150 * 151 * @return Removed element or null if any 152 */ 153 public T removeFirst() { 154 if (isEmpty()) { 155 return null; 156 } 157 T firstElement = elements[firstIndex]; 158 elements[firstIndex] = null; 159 firstIndex = incrementIndex(firstIndex, 0); 160 return firstElement; 161 } 162 163 /** 164 * Returns the first (inserted) element. 165 * 166 * @return First element or null if any 167 */ 168 public T first() { 169 return isEmpty() ? null : elements[lastIndex]; 170 } 171 172 /** 173 * Returns the last (inserted) element. 174 * 175 * @return Last element or null if any 176 */ 177 public T last() { 178 return isEmpty() ? null : elements[lastIndex]; 179 } 180 181 /** Removes all elements from the list. */ 182 @Override 183 public void clear() { 184 Arrays.fill(elements, null); 185 lastIndex = 0; 186 firstIndex = -1; 187 size = 0; 188 } 189 190 /** 191 * Creates a new iterator to browse elements. 192 * 193 * @return Iterator 194 */ 195 @Override 196 public java.util.Iterator<T> iterator() { 197 if (isEmpty()) { 198 return new EmptyIterator(); 199 } else { 200 return new MainIterator(); 201 } 202 } 203 204 /** Empty iterator used when the list is empty. */ 205 private class EmptyIterator implements Iterator<T> { 206 207 public boolean hasNext() { 208 return false; 209 } 210 211 public T next() { 212 throw new UnsupportedOperationException("Not supported yet."); 213 } 214 215 public void remove() { 216 throw new UnsupportedOperationException("Not supported yet."); 217 } 218 } 219 220 /** Main iterator user when the list contains at least one element. */ 221 private class MainIterator implements Iterator<T> { 222 223 /** Index of the element. */ 224 private int nextIndex = firstIndex; 225 /** 226 * Is it first element. This flag is required because when 227 * firstIndex=lastIndex (ring buffer is completely used) we 228 * don't whether it's the first iteration or the last one. 229 */ 230 private boolean begin = true; 231 232 /** Is there another element in the list. */ 233 public boolean hasNext() { 234 return begin || nextIndex != lastIndex; 235 } 236 237 /** Returns an element and compute the next one. */ 238 public T next() { 239 T nextElement = elements[nextIndex]; 240 begin = false; 241 nextIndex = incrementIndex(nextIndex, 0); 242 return nextElement; 243 } 244 245 public void remove() { 246 throw new UnsupportedOperationException("Not supported"); 247 } 248 } 249 250 /** 251 * Copy elements in a target array. 252 * 253 * @param elementsCopy Target array 254 */ 255 private <X> void copy(X[] elementsCopy) { 256 if (!isEmpty()) { 257 if (firstIndex < lastIndex) { 258 System.arraycopy(elements, firstIndex, elementsCopy, 0, lastIndex - firstIndex); 259 } else { 260 int firstSize = elements.length - firstIndex; 261 System.arraycopy(elements, firstIndex, elementsCopy, 0, firstSize); 262 System.arraycopy(elements, 0, elementsCopy, firstSize, lastIndex); 263 } 264 } 265 } 266 267 @Override 268 public Object[] toArray() { 269 Object[] elementsCopy = new Object[size]; 270 copy(elementsCopy); 271 return elementsCopy; 272 } 273 274 @Override 275 public <T> T[] toArray(T[] elementsCopy) { 276 if (elementsCopy.length >= size) { 277 copy(elementsCopy); 278 return elementsCopy; 279 } else { 280 return (T[]) toArray(); 281 } 282 } 283}