• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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