1 /******************************************************************************* 2 * Copyright 2015 See AUTHORS file. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 ******************************************************************************/ 16 17 package com.badlogic.gdx.utils; 18 19 import java.util.Iterator; 20 import java.util.NoSuchElementException; 21 22 import com.badlogic.gdx.utils.Array.ArrayIterable; 23 import com.badlogic.gdx.utils.reflect.ArrayReflection; 24 25 /** A resizable, ordered array of objects with efficient add and remove at the beginning and end. Values in the backing array may 26 * wrap back to the beginning, making add and remove at the beginning and end O(1) (unless the backing array needs to resize when 27 * adding). Deque functionality is provided via {@link #removeLast()} and {@link #addFirst(Object)}. */ 28 public class Queue<T> implements Iterable<T> { 29 /** Contains the values in the queue. Head and tail indices go in a circle around this array, wrapping at the end. */ 30 protected T[] values; 31 32 /** Index of first element. Logically smaller than tail. Unless empty, it points to a valid element inside queue. */ 33 protected int head = 0; 34 35 /** Index of last element. Logically bigger than head. Usually points to an empty position, but points to the head when full 36 * (size == values.length). */ 37 protected int tail = 0; 38 39 /** Number of elements in the queue. */ 40 public int size = 0; 41 42 private QueueIterable iterable; 43 44 /** Creates a new Queue which can hold 16 values without needing to resize backing array. */ Queue()45 public Queue () { 46 this(16); 47 } 48 49 /** Creates a new Queue which can hold the specified number of values without needing to resize backing array. */ Queue(int initialSize)50 public Queue (int initialSize) { 51 // noinspection unchecked 52 this.values = (T[])new Object[initialSize]; 53 } 54 55 /** Creates a new Queue which can hold the specified number of values without needing to resize backing array. This creates 56 * backing array of the specified type via reflection, which is necessary only when accessing the backing array directly. */ Queue(int initialSize, Class<T> type)57 public Queue (int initialSize, Class<T> type) { 58 // noinspection unchecked 59 this.values = (T[])ArrayReflection.newInstance(type, initialSize); 60 } 61 62 /** Append given object to the tail. (enqueue to tail) Unless backing array needs resizing, operates in O(1) time. 63 * @param object can be null */ addLast(T object)64 public void addLast (T object) { 65 T[] values = this.values; 66 67 if (size == values.length) { 68 resize(values.length << 1);// * 2 69 values = this.values; 70 } 71 72 values[tail++] = object; 73 if (tail == values.length) { 74 tail = 0; 75 } 76 size++; 77 } 78 79 /** Prepend given object to the head. (enqueue to head) Unless backing array needs resizing, operates in O(1) time. 80 * @see #addLast(Object) 81 * @param object can be null */ addFirst(T object)82 public void addFirst (T object) { 83 T[] values = this.values; 84 85 if (size == values.length) { 86 resize(values.length << 1);// * 2 87 values = this.values; 88 } 89 90 int head = this.head; 91 head--; 92 if (head == -1) { 93 head = values.length - 1; 94 } 95 values[head] = object; 96 97 this.head = head; 98 this.size++; 99 } 100 101 /** Increases the size of the backing array to accommodate the specified number of additional items. Useful before adding many 102 * items to avoid multiple backing array resizes. */ ensureCapacity(int additional)103 public void ensureCapacity (int additional) { 104 final int needed = size + additional; 105 if (values.length < needed) { 106 resize(needed); 107 } 108 } 109 110 /** Resize backing array. newSize must be bigger than current size. */ resize(int newSize)111 protected void resize (int newSize) { 112 final T[] values = this.values; 113 final int head = this.head; 114 final int tail = this.tail; 115 116 @SuppressWarnings("unchecked") 117 final T[] newArray = (T[])ArrayReflection.newInstance(values.getClass().getComponentType(), newSize); 118 if (head < tail) { 119 // Continuous 120 System.arraycopy(values, head, newArray, 0, tail - head); 121 } else if (size > 0) { 122 // Wrapped 123 final int rest = values.length - head; 124 System.arraycopy(values, head, newArray, 0, rest); 125 System.arraycopy(values, 0, newArray, rest, tail); 126 } 127 this.values = newArray; 128 this.head = 0; 129 this.tail = size; 130 } 131 132 /** Remove the first item from the queue. (dequeue from head) Always O(1). 133 * @return removed object 134 * @throws NoSuchElementException when queue is empty */ removeFirst()135 public T removeFirst () { 136 if (size == 0) { 137 // Underflow 138 throw new NoSuchElementException("Queue is empty."); 139 } 140 141 final T[] values = this.values; 142 143 final T result = values[head]; 144 values[head] = null; 145 head++; 146 if (head == values.length) { 147 head = 0; 148 } 149 size--; 150 151 return result; 152 } 153 154 /** Remove the last item from the queue. (dequeue from tail) Always O(1). 155 * @see #removeFirst() 156 * @return removed object 157 * @throws NoSuchElementException when queue is empty */ removeLast()158 public T removeLast () { 159 if (size == 0) { 160 throw new NoSuchElementException("Queue is empty."); 161 } 162 163 final T[] values = this.values; 164 int tail = this.tail; 165 tail--; 166 if (tail == -1) { 167 tail = values.length - 1; 168 } 169 final T result = values[tail]; 170 values[tail] = null; 171 this.tail = tail; 172 size--; 173 174 return result; 175 } 176 177 /** Returns the index of first occurrence of value in the queue, or -1 if no such value exists. 178 * @param identity If true, == comparison will be used. If false, .equals() comparison will be used. 179 * @return An index of first occurrence of value in queue or -1 if no such value exists */ indexOf(T value, boolean identity)180 public int indexOf (T value, boolean identity) { 181 if (size == 0) return -1; 182 T[] values = this.values; 183 int head = this.head, tail = this.tail; 184 if (identity || value == null) { 185 if (head < tail) { 186 for (int i = head, n = tail; i < n; i++) 187 if (values[i] == value) return i; 188 } else { 189 for (int i = head, n = values.length; i < n; i++) 190 if (values[i] == value) return i - head; 191 for (int i = 0, n = tail; i < n; i++) 192 if (values[i] == value) return i + values.length - head; 193 } 194 } else { 195 if (head < tail) { 196 for (int i = head, n = tail; i < n; i++) 197 if (value.equals(values[i])) return i; 198 } else { 199 for (int i = head, n = values.length; i < n; i++) 200 if (value.equals(values[i])) return i - head; 201 for (int i = 0, n = tail; i < n; i++) 202 if (value.equals(values[i])) return i + values.length - head; 203 } 204 } 205 return -1; 206 } 207 208 /** Removes the first instance of the specified value in the queue. 209 * @param identity If true, == comparison will be used. If false, .equals() comparison will be used. 210 * @return true if value was found and removed, false otherwise */ removeValue(T value, boolean identity)211 public boolean removeValue (T value, boolean identity) { 212 int index = indexOf(value, identity); 213 if (index == -1) return false; 214 removeIndex(index); 215 return true; 216 } 217 218 /** Removes and returns the item at the specified index. */ removeIndex(int index)219 public T removeIndex (int index) { 220 if (index < 0) throw new IndexOutOfBoundsException("index can't be < 0: " + index); 221 if (index >= size) throw new IndexOutOfBoundsException("index can't be >= size: " + index + " >= " + size); 222 223 T[] values = this.values; 224 int head = this.head, tail = this.tail; 225 index += head; 226 T value; 227 if (head < tail) { // index is between head and tail. 228 value = (T)values[index]; 229 System.arraycopy(values, index + 1, values, index, tail - index); 230 values[tail] = null; 231 this.tail--; 232 } else if (index >= values.length) { // index is between 0 and tail. 233 index -= values.length; 234 value = (T)values[index]; 235 System.arraycopy(values, index + 1, values, index, tail - index); 236 this.tail--; 237 } else { // index is between head and values.length. 238 value = (T)values[index]; 239 System.arraycopy(values, head, values, head + 1, index - head); 240 values[head] = null; 241 this.head++; 242 } 243 size--; 244 return value; 245 } 246 247 /** Returns the first (head) item in the queue (without removing it). 248 * @see #addFirst(Object) 249 * @see #removeFirst() 250 * @throws NoSuchElementException when queue is empty */ first()251 public T first () { 252 if (size == 0) { 253 // Underflow 254 throw new NoSuchElementException("Queue is empty."); 255 } 256 return values[head]; 257 } 258 259 /** Returns the last (tail) item in the queue (without removing it). 260 * @see #addLast(Object) 261 * @see #removeLast() 262 * @throws NoSuchElementException when queue is empty */ last()263 public T last () { 264 if (size == 0) { 265 // Underflow 266 throw new NoSuchElementException("Queue is empty."); 267 } 268 final T[] values = this.values; 269 int tail = this.tail; 270 tail--; 271 if (tail == -1) { 272 tail = values.length - 1; 273 } 274 return values[tail]; 275 } 276 277 /** Retrieves the value in queue without removing it. Indexing is from the front to back, zero based. Therefore get(0) is the 278 * same as {@link #first()}. 279 * @throws IndexOutOfBoundsException when the index is negative or >= size */ get(int index)280 public T get (int index) { 281 if (index < 0) throw new IndexOutOfBoundsException("index can't be < 0: " + index); 282 if (index >= size) throw new IndexOutOfBoundsException("index can't be >= size: " + index + " >= " + size); 283 final T[] values = this.values; 284 285 int i = head + index; 286 if (i >= values.length) { 287 i -= values.length; 288 } 289 return values[i]; 290 } 291 292 /** Removes all values from this queue. Values in backing array are set to null to prevent memory leak, so this operates in 293 * O(n). */ clear()294 public void clear () { 295 if (size == 0) return; 296 final T[] values = this.values; 297 final int head = this.head; 298 final int tail = this.tail; 299 300 if (head < tail) { 301 // Continuous 302 for (int i = head; i < tail; i++) { 303 values[i] = null; 304 } 305 } else { 306 // Wrapped 307 for (int i = head; i < values.length; i++) { 308 values[i] = null; 309 } 310 for (int i = 0; i < tail; i++) { 311 values[i] = null; 312 } 313 } 314 this.head = 0; 315 this.tail = 0; 316 this.size = 0; 317 } 318 319 /** Returns an iterator for the items in the queue. Remove is supported. Note that the same iterator instance is returned each 320 * time this method is called. Use the {@link QueueIterator} constructor for nested or multithreaded iteration. */ iterator()321 public Iterator<T> iterator () { 322 if (iterable == null) iterable = new QueueIterable(this); 323 return iterable.iterator(); 324 } 325 toString()326 public String toString () { 327 if (size == 0) { 328 return "[]"; 329 } 330 final T[] values = this.values; 331 final int head = this.head; 332 final int tail = this.tail; 333 334 StringBuilder sb = new StringBuilder(64); 335 sb.append('['); 336 sb.append(values[head]); 337 for (int i = (head + 1) % values.length; i != tail; i = (i + 1) % values.length) { 338 sb.append(", ").append(values[i]); 339 } 340 sb.append(']'); 341 return sb.toString(); 342 } 343 hashCode()344 public int hashCode () { 345 final int size = this.size; 346 final T[] values = this.values; 347 final int backingLength = values.length; 348 int index = this.head; 349 350 int hash = size + 1; 351 for (int s = 0; s < size; s++) { 352 final T value = values[index]; 353 354 hash *= 31; 355 if (value != null) hash += value.hashCode(); 356 357 index++; 358 if (index == backingLength) index = 0; 359 } 360 361 return hash; 362 } 363 equals(Object o)364 public boolean equals (Object o) { 365 if (this == o) return true; 366 if (o == null || !(o instanceof Queue)) return false; 367 368 Queue<?> q = (Queue<?>)o; 369 final int size = this.size; 370 371 if (q.size != size) return false; 372 373 final T[] myValues = this.values; 374 final int myBackingLength = myValues.length; 375 final Object[] itsValues = q.values; 376 final int itsBackingLength = itsValues.length; 377 378 int myIndex = head; 379 int itsIndex = q.head; 380 for (int s = 0; s < size; s++) { 381 T myValue = myValues[myIndex]; 382 Object itsValue = itsValues[itsIndex]; 383 384 if (!(myValue == null ? itsValue == null : myValue.equals(itsValue))) return false; 385 myIndex++; 386 itsIndex++; 387 if (myIndex == myBackingLength) myIndex = 0; 388 if (itsIndex == itsBackingLength) itsIndex = 0; 389 } 390 return true; 391 } 392 393 static public class QueueIterator<T> implements Iterator<T>, Iterable<T> { 394 private final Queue<T> queue; 395 private final boolean allowRemove; 396 int index; 397 boolean valid = true; 398 399 // QueueIterable<T> iterable; 400 QueueIterator(Queue<T> queue)401 public QueueIterator (Queue<T> queue) { 402 this(queue, true); 403 } 404 QueueIterator(Queue<T> queue, boolean allowRemove)405 public QueueIterator (Queue<T> queue, boolean allowRemove) { 406 this.queue = queue; 407 this.allowRemove = allowRemove; 408 } 409 hasNext()410 public boolean hasNext () { 411 if (!valid) { 412 // System.out.println(iterable.lastAcquire); 413 throw new GdxRuntimeException("#iterator() cannot be used nested."); 414 } 415 return index < queue.size; 416 } 417 next()418 public T next () { 419 if (index >= queue.size) throw new NoSuchElementException(String.valueOf(index)); 420 if (!valid) { 421 // System.out.println(iterable.lastAcquire); 422 throw new GdxRuntimeException("#iterator() cannot be used nested."); 423 } 424 return queue.get(index++); 425 } 426 remove()427 public void remove () { 428 if (!allowRemove) throw new GdxRuntimeException("Remove not allowed."); 429 index--; 430 queue.removeIndex(index); 431 } 432 reset()433 public void reset () { 434 index = 0; 435 } 436 iterator()437 public Iterator<T> iterator () { 438 return this; 439 } 440 } 441 442 static public class QueueIterable<T> implements Iterable<T> { 443 private final Queue<T> queue; 444 private final boolean allowRemove; 445 private QueueIterator iterator1, iterator2; 446 447 // java.io.StringWriter lastAcquire = new java.io.StringWriter(); 448 QueueIterable(Queue<T> queue)449 public QueueIterable (Queue<T> queue) { 450 this(queue, true); 451 } 452 QueueIterable(Queue<T> queue, boolean allowRemove)453 public QueueIterable (Queue<T> queue, boolean allowRemove) { 454 this.queue = queue; 455 this.allowRemove = allowRemove; 456 } 457 iterator()458 public Iterator<T> iterator () { 459 // lastAcquire.getBuffer().setLength(0); 460 // new Throwable().printStackTrace(new java.io.PrintWriter(lastAcquire)); 461 if (iterator1 == null) { 462 iterator1 = new QueueIterator(queue, allowRemove); 463 iterator2 = new QueueIterator(queue, allowRemove); 464 // iterator1.iterable = this; 465 // iterator2.iterable = this; 466 } 467 if (!iterator1.valid) { 468 iterator1.index = 0; 469 iterator1.valid = true; 470 iterator2.valid = false; 471 return iterator1; 472 } 473 iterator2.index = 0; 474 iterator2.valid = true; 475 iterator1.valid = false; 476 return iterator2; 477 } 478 } 479 } 480