• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*******************************************************************************
2  * Copyright 2011 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.Comparator;
20 import java.util.Iterator;
21 import java.util.NoSuchElementException;
22 
23 import com.badlogic.gdx.math.MathUtils;
24 import com.badlogic.gdx.utils.reflect.ArrayReflection;
25 
26 /** A resizable, ordered or unordered array of objects. If unordered, this class avoids a memory copy when removing elements (the
27  * last element is moved to the removed element's position).
28  * @author Nathan Sweet */
29 public class Array<T> implements Iterable<T> {
30 	/** Provides direct access to the underlying array. If the Array's generic type is not Object, this field may only be accessed
31 	 * if the {@link Array#Array(boolean, int, Class)} constructor was used. */
32 	public T[] items;
33 
34 	public int size;
35 	public boolean ordered;
36 
37 	private ArrayIterable iterable;
38 	private Predicate.PredicateIterable<T> predicateIterable;
39 
40 	/** Creates an ordered array with a capacity of 16. */
Array()41 	public Array () {
42 		this(true, 16);
43 	}
44 
45 	/** Creates an ordered array with the specified capacity. */
Array(int capacity)46 	public Array (int capacity) {
47 		this(true, capacity);
48 	}
49 
50 	/** @param ordered If false, methods that remove elements may change the order of other elements in the array, which avoids a
51 	 *           memory copy.
52 	 * @param capacity Any elements added beyond this will cause the backing array to be grown. */
Array(boolean ordered, int capacity)53 	public Array (boolean ordered, int capacity) {
54 		this.ordered = ordered;
55 		items = (T[])new Object[capacity];
56 	}
57 
58 	/** Creates a new array with {@link #items} of the specified type.
59 	 * @param ordered If false, methods that remove elements may change the order of other elements in the array, which avoids a
60 	 *           memory copy.
61 	 * @param capacity Any elements added beyond this will cause the backing array to be grown. */
Array(boolean ordered, int capacity, Class arrayType)62 	public Array (boolean ordered, int capacity, Class arrayType) {
63 		this.ordered = ordered;
64 		items = (T[])ArrayReflection.newInstance(arrayType, capacity);
65 	}
66 
67 	/** Creates an ordered array with {@link #items} of the specified type and a capacity of 16. */
Array(Class arrayType)68 	public Array (Class arrayType) {
69 		this(true, 16, arrayType);
70 	}
71 
72 	/** Creates a new array containing the elements in the specified array. The new array will have the same type of backing array
73 	 * and will be ordered if the specified array is ordered. The capacity is set to the number of elements, so any subsequent
74 	 * elements added will cause the backing array to be grown. */
Array(Array<? extends T> array)75 	public Array (Array<? extends T> array) {
76 		this(array.ordered, array.size, array.items.getClass().getComponentType());
77 		size = array.size;
78 		System.arraycopy(array.items, 0, items, 0, size);
79 	}
80 
81 	/** Creates a new ordered array containing the elements in the specified array. The new array will have the same type of
82 	 * backing array. The capacity is set to the number of elements, so any subsequent elements added will cause the backing array
83 	 * to be grown. */
Array(T[] array)84 	public Array (T[] array) {
85 		this(true, array, 0, array.length);
86 	}
87 
88 	/** Creates a new array containing the elements in the specified array. The new array will have the same type of backing array.
89 	 * The capacity is set to the number of elements, so any subsequent elements added will cause the backing array to be grown.
90 	 * @param ordered If false, methods that remove elements may change the order of other elements in the array, which avoids a
91 	 *           memory copy. */
Array(boolean ordered, T[] array, int start, int count)92 	public Array (boolean ordered, T[] array, int start, int count) {
93 		this(ordered, count, (Class)array.getClass().getComponentType());
94 		size = count;
95 		System.arraycopy(array, start, items, 0, size);
96 	}
97 
add(T value)98 	public void add (T value) {
99 		T[] items = this.items;
100 		if (size == items.length) items = resize(Math.max(8, (int)(size * 1.75f)));
101 		items[size++] = value;
102 	}
103 
addAll(Array<? extends T> array)104 	public void addAll (Array<? extends T> array) {
105 		addAll(array, 0, array.size);
106 	}
107 
addAll(Array<? extends T> array, int start, int count)108 	public void addAll (Array<? extends T> array, int start, int count) {
109 		if (start + count > array.size)
110 			throw new IllegalArgumentException("start + count must be <= size: " + start + " + " + count + " <= " + array.size);
111 		addAll((T[])array.items, start, count);
112 	}
113 
addAll(T... array)114 	public void addAll (T... array) {
115 		addAll(array, 0, array.length);
116 	}
117 
addAll(T[] array, int start, int count)118 	public void addAll (T[] array, int start, int count) {
119 		T[] items = this.items;
120 		int sizeNeeded = size + count;
121 		if (sizeNeeded > items.length) items = resize(Math.max(8, (int)(sizeNeeded * 1.75f)));
122 		System.arraycopy(array, start, items, size, count);
123 		size += count;
124 	}
125 
get(int index)126 	public T get (int index) {
127 		if (index >= size) throw new IndexOutOfBoundsException("index can't be >= size: " + index + " >= " + size);
128 		return items[index];
129 	}
130 
set(int index, T value)131 	public void set (int index, T value) {
132 		if (index >= size) throw new IndexOutOfBoundsException("index can't be >= size: " + index + " >= " + size);
133 		items[index] = value;
134 	}
135 
insert(int index, T value)136 	public void insert (int index, T value) {
137 		if (index > size) throw new IndexOutOfBoundsException("index can't be > size: " + index + " > " + size);
138 		T[] items = this.items;
139 		if (size == items.length) items = resize(Math.max(8, (int)(size * 1.75f)));
140 		if (ordered)
141 			System.arraycopy(items, index, items, index + 1, size - index);
142 		else
143 			items[size] = items[index];
144 		size++;
145 		items[index] = value;
146 	}
147 
swap(int first, int second)148 	public void swap (int first, int second) {
149 		if (first >= size) throw new IndexOutOfBoundsException("first can't be >= size: " + first + " >= " + size);
150 		if (second >= size) throw new IndexOutOfBoundsException("second can't be >= size: " + second + " >= " + size);
151 		T[] items = this.items;
152 		T firstValue = items[first];
153 		items[first] = items[second];
154 		items[second] = firstValue;
155 	}
156 
157 	/** Returns if this array contains value.
158 	 * @param value May be null.
159 	 * @param identity If true, == comparison will be used. If false, .equals() comparison will be used.
160 	 * @return true if array contains value, false if it doesn't */
contains(T value, boolean identity)161 	public boolean contains (T value, boolean identity) {
162 		T[] items = this.items;
163 		int i = size - 1;
164 		if (identity || value == null) {
165 			while (i >= 0)
166 				if (items[i--] == value) return true;
167 		} else {
168 			while (i >= 0)
169 				if (value.equals(items[i--])) return true;
170 		}
171 		return false;
172 	}
173 
174 	/** Returns the index of first occurrence of value in the array, or -1 if no such value exists.
175 	 * @param value May be null.
176 	 * @param identity If true, == comparison will be used. If false, .equals() comparison will be used.
177 	 * @return An index of first occurrence of value in array or -1 if no such value exists */
indexOf(T value, boolean identity)178 	public int indexOf (T value, boolean identity) {
179 		T[] items = this.items;
180 		if (identity || value == null) {
181 			for (int i = 0, n = size; i < n; i++)
182 				if (items[i] == value) return i;
183 		} else {
184 			for (int i = 0, n = size; i < n; i++)
185 				if (value.equals(items[i])) return i;
186 		}
187 		return -1;
188 	}
189 
190 	/** Returns an index of last occurrence of value in array or -1 if no such value exists. Search is started from the end of an
191 	 * array.
192 	 * @param value May be null.
193 	 * @param identity If true, == comparison will be used. If false, .equals() comparison will be used.
194 	 * @return An index of last occurrence of value in array or -1 if no such value exists */
lastIndexOf(T value, boolean identity)195 	public int lastIndexOf (T value, boolean identity) {
196 		T[] items = this.items;
197 		if (identity || value == null) {
198 			for (int i = size - 1; i >= 0; i--)
199 				if (items[i] == value) return i;
200 		} else {
201 			for (int i = size - 1; i >= 0; i--)
202 				if (value.equals(items[i])) return i;
203 		}
204 		return -1;
205 	}
206 
207 	/** Removes the first instance of the specified value in the array.
208 	 * @param value May be null.
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 		T[] items = this.items;
213 		if (identity || value == null) {
214 			for (int i = 0, n = size; i < n; i++) {
215 				if (items[i] == value) {
216 					removeIndex(i);
217 					return true;
218 				}
219 			}
220 		} else {
221 			for (int i = 0, n = size; i < n; i++) {
222 				if (value.equals(items[i])) {
223 					removeIndex(i);
224 					return true;
225 				}
226 			}
227 		}
228 		return false;
229 	}
230 
231 	/** Removes and returns the item at the specified index. */
removeIndex(int index)232 	public T removeIndex (int index) {
233 		if (index >= size) throw new IndexOutOfBoundsException("index can't be >= size: " + index + " >= " + size);
234 		T[] items = this.items;
235 		T value = (T)items[index];
236 		size--;
237 		if (ordered)
238 			System.arraycopy(items, index + 1, items, index, size - index);
239 		else
240 			items[index] = items[size];
241 		items[size] = null;
242 		return value;
243 	}
244 
245 	/** Removes the items between the specified indices, inclusive. */
removeRange(int start, int end)246 	public void removeRange (int start, int end) {
247 		if (end >= size) throw new IndexOutOfBoundsException("end can't be >= size: " + end + " >= " + size);
248 		if (start > end) throw new IndexOutOfBoundsException("start can't be > end: " + start + " > " + end);
249 		T[] items = this.items;
250 		int count = end - start + 1;
251 		if (ordered)
252 			System.arraycopy(items, start + count, items, start, size - (start + count));
253 		else {
254 			int lastIndex = this.size - 1;
255 			for (int i = 0; i < count; i++)
256 				items[start + i] = items[lastIndex - i];
257 		}
258 		size -= count;
259 	}
260 
261 	/** Removes from this array all of elements contained in the specified array.
262 	 * @param identity True to use ==, false to use .equals().
263 	 * @return true if this array was modified. */
removeAll(Array<? extends T> array, boolean identity)264 	public boolean removeAll (Array<? extends T> array, boolean identity) {
265 		int size = this.size;
266 		int startSize = size;
267 		T[] items = this.items;
268 		if (identity) {
269 			for (int i = 0, n = array.size; i < n; i++) {
270 				T item = array.get(i);
271 				for (int ii = 0; ii < size; ii++) {
272 					if (item == items[ii]) {
273 						removeIndex(ii);
274 						size--;
275 						break;
276 					}
277 				}
278 			}
279 		} else {
280 			for (int i = 0, n = array.size; i < n; i++) {
281 				T item = array.get(i);
282 				for (int ii = 0; ii < size; ii++) {
283 					if (item.equals(items[ii])) {
284 						removeIndex(ii);
285 						size--;
286 						break;
287 					}
288 				}
289 			}
290 		}
291 		return size != startSize;
292 	}
293 
294 	/** Removes and returns the last item. */
pop()295 	public T pop () {
296 		if (size == 0) throw new IllegalStateException("Array is empty.");
297 		--size;
298 		T item = items[size];
299 		items[size] = null;
300 		return item;
301 	}
302 
303 	/** Returns the last item. */
peek()304 	public T peek () {
305 		if (size == 0) throw new IllegalStateException("Array is empty.");
306 		return items[size - 1];
307 	}
308 
309 	/** Returns the first item. */
first()310 	public T first () {
311 		if (size == 0) throw new IllegalStateException("Array is empty.");
312 		return items[0];
313 	}
314 
clear()315 	public void clear () {
316 		T[] items = this.items;
317 		for (int i = 0, n = size; i < n; i++)
318 			items[i] = null;
319 		size = 0;
320 	}
321 
322 	/** Reduces the size of the backing array to the size of the actual items. This is useful to release memory when many items
323 	 * have been removed, or if it is known that more items will not be added.
324 	 * @return {@link #items} */
shrink()325 	public T[] shrink () {
326 		if (items.length != size) resize(size);
327 		return items;
328 	}
329 
330 	/** Increases the size of the backing array to accommodate the specified number of additional items. Useful before adding many
331 	 * items to avoid multiple backing array resizes.
332 	 * @return {@link #items} */
ensureCapacity(int additionalCapacity)333 	public T[] ensureCapacity (int additionalCapacity) {
334 		int sizeNeeded = size + additionalCapacity;
335 		if (sizeNeeded > items.length) resize(Math.max(8, sizeNeeded));
336 		return items;
337 	}
338 
339 	/** Sets the array size, leaving any values beyond the current size null.
340 	 * @return {@link #items} */
setSize(int newSize)341 	public T[] setSize (int newSize) {
342 		truncate(newSize);
343 		if (newSize > items.length) resize(Math.max(8, newSize));
344 		size = newSize;
345 		return items;
346 	}
347 
348 	/** Creates a new backing array with the specified size containing the current items. */
resize(int newSize)349 	protected T[] resize (int newSize) {
350 		T[] items = this.items;
351 		T[] newItems = (T[])ArrayReflection.newInstance(items.getClass().getComponentType(), newSize);
352 		System.arraycopy(items, 0, newItems, 0, Math.min(size, newItems.length));
353 		this.items = newItems;
354 		return newItems;
355 	}
356 
357 	/** Sorts this array. The array elements must implement {@link Comparable}. This method is not thread safe (uses
358 	 * {@link Sort#instance()}). */
sort()359 	public void sort () {
360 		Sort.instance().sort(items, 0, size);
361 	}
362 
363 	/** Sorts the array. This method is not thread safe (uses {@link Sort#instance()}). */
sort(Comparator<? super T> comparator)364 	public void sort (Comparator<? super T> comparator) {
365 		Sort.instance().sort(items, comparator, 0, size);
366 	}
367 
368 	/** Selects the nth-lowest element from the Array according to Comparator ranking. This might partially sort the Array. The
369 	 * array must have a size greater than 0, or a {@link com.badlogic.gdx.utils.GdxRuntimeException} will be thrown.
370 	 * @see Select
371 	 * @param comparator used for comparison
372 	 * @param kthLowest rank of desired object according to comparison, n is based on ordinal numbers, not array indices. for min
373 	 *           value use 1, for max value use size of array, using 0 results in runtime exception.
374 	 * @return the value of the Nth lowest ranked object. */
selectRanked(Comparator<T> comparator, int kthLowest)375 	public T selectRanked (Comparator<T> comparator, int kthLowest) {
376 		if (kthLowest < 1) {
377 			throw new GdxRuntimeException("nth_lowest must be greater than 0, 1 = first, 2 = second...");
378 		}
379 		return Select.instance().select(items, comparator, kthLowest, size);
380 	}
381 
382 	/** @see Array#selectRanked(java.util.Comparator, int)
383 	 * @param comparator used for comparison
384 	 * @param kthLowest rank of desired object according to comparison, n is based on ordinal numbers, not array indices. for min
385 	 *           value use 1, for max value use size of array, using 0 results in runtime exception.
386 	 * @return the index of the Nth lowest ranked object. */
selectRankedIndex(Comparator<T> comparator, int kthLowest)387 	public int selectRankedIndex (Comparator<T> comparator, int kthLowest) {
388 		if (kthLowest < 1) {
389 			throw new GdxRuntimeException("nth_lowest must be greater than 0, 1 = first, 2 = second...");
390 		}
391 		return Select.instance().selectIndex(items, comparator, kthLowest, size);
392 	}
393 
reverse()394 	public void reverse () {
395 		T[] items = this.items;
396 		for (int i = 0, lastIndex = size - 1, n = size / 2; i < n; i++) {
397 			int ii = lastIndex - i;
398 			T temp = items[i];
399 			items[i] = items[ii];
400 			items[ii] = temp;
401 		}
402 	}
403 
shuffle()404 	public void shuffle () {
405 		T[] items = this.items;
406 		for (int i = size - 1; i >= 0; i--) {
407 			int ii = MathUtils.random(i);
408 			T temp = items[i];
409 			items[i] = items[ii];
410 			items[ii] = temp;
411 		}
412 	}
413 
414 	/** Returns an iterator for the items in the array. Remove is supported. Note that the same iterator instance is returned each
415 	 * time this method is called. Use the {@link ArrayIterator} constructor for nested or multithreaded iteration. */
iterator()416 	public Iterator<T> iterator () {
417 		if (iterable == null) iterable = new ArrayIterable(this);
418 		return iterable.iterator();
419 	}
420 
421 	/** Returns an iterable for the selected items in the array. Remove is supported, but not between hasNext() and next(). Note
422 	 * that the same iterable instance is returned each time this method is called. Use the {@link Predicate.PredicateIterable}
423 	 * constructor for nested or multithreaded iteration. */
select(Predicate<T> predicate)424 	public Iterable<T> select (Predicate<T> predicate) {
425 		if (predicateIterable == null)
426 			predicateIterable = new Predicate.PredicateIterable<T>(this, predicate);
427 		else
428 			predicateIterable.set(this, predicate);
429 		return predicateIterable;
430 	}
431 
432 	/** Reduces the size of the array to the specified size. If the array is already smaller than the specified size, no action is
433 	 * taken. */
truncate(int newSize)434 	public void truncate (int newSize) {
435 		if (size <= newSize) return;
436 		for (int i = newSize; i < size; i++)
437 			items[i] = null;
438 		size = newSize;
439 	}
440 
441 	/** Returns a random item from the array, or null if the array is empty. */
random()442 	public T random () {
443 		if (size == 0) return null;
444 		return items[MathUtils.random(0, size - 1)];
445 	}
446 
447 	/** Returns the items as an array. Note the array is typed, so the {@link #Array(Class)} constructor must have been used.
448 	 * Otherwise use {@link #toArray(Class)} to specify the array type. */
toArray()449 	public T[] toArray () {
450 		return (T[])toArray(items.getClass().getComponentType());
451 	}
452 
toArray(Class type)453 	public <V> V[] toArray (Class type) {
454 		V[] result = (V[])ArrayReflection.newInstance(type, size);
455 		System.arraycopy(items, 0, result, 0, size);
456 		return result;
457 	}
458 
hashCode()459 	public int hashCode () {
460 		if (!ordered) return super.hashCode();
461 		Object[] items = this.items;
462 		int h = 1;
463 		for (int i = 0, n = size; i < n; i++) {
464 			h *= 31;
465 			Object item = items[i];
466 			if (item != null) h += item.hashCode();
467 		}
468 		return h;
469 	}
470 
equals(Object object)471 	public boolean equals (Object object) {
472 		if (object == this) return true;
473 		if (!ordered) return false;
474 		if (!(object instanceof Array)) return false;
475 		Array array = (Array)object;
476 		if (!array.ordered) return false;
477 		int n = size;
478 		if (n != array.size) return false;
479 		Object[] items1 = this.items;
480 		Object[] items2 = array.items;
481 		for (int i = 0; i < n; i++) {
482 			Object o1 = items1[i];
483 			Object o2 = items2[i];
484 			if (!(o1 == null ? o2 == null : o1.equals(o2))) return false;
485 		}
486 		return true;
487 	}
488 
toString()489 	public String toString () {
490 		if (size == 0) return "[]";
491 		T[] items = this.items;
492 		StringBuilder buffer = new StringBuilder(32);
493 		buffer.append('[');
494 		buffer.append(items[0]);
495 		for (int i = 1; i < size; i++) {
496 			buffer.append(", ");
497 			buffer.append(items[i]);
498 		}
499 		buffer.append(']');
500 		return buffer.toString();
501 	}
502 
toString(String separator)503 	public String toString (String separator) {
504 		if (size == 0) return "";
505 		T[] items = this.items;
506 		StringBuilder buffer = new StringBuilder(32);
507 		buffer.append(items[0]);
508 		for (int i = 1; i < size; i++) {
509 			buffer.append(separator);
510 			buffer.append(items[i]);
511 		}
512 		return buffer.toString();
513 	}
514 
515 	/** @see #Array(Class) */
of(Class<T> arrayType)516 	static public <T> Array<T> of (Class<T> arrayType) {
517 		return new Array<T>(arrayType);
518 	}
519 
520 	/** @see #Array(boolean, int, Class) */
of(boolean ordered, int capacity, Class<T> arrayType)521 	static public <T> Array<T> of (boolean ordered, int capacity, Class<T> arrayType) {
522 		return new Array<T>(ordered, capacity, arrayType);
523 	}
524 
525 	/** @see #Array(Object[]) */
with(T... array)526 	static public <T> Array<T> with (T... array) {
527 		return new Array(array);
528 	}
529 
530 	static public class ArrayIterator<T> implements Iterator<T>, Iterable<T> {
531 		private final Array<T> array;
532 		private final boolean allowRemove;
533 		int index;
534 		boolean valid = true;
535 
536 // ArrayIterable<T> iterable;
537 
ArrayIterator(Array<T> array)538 		public ArrayIterator (Array<T> array) {
539 			this(array, true);
540 		}
541 
ArrayIterator(Array<T> array, boolean allowRemove)542 		public ArrayIterator (Array<T> array, boolean allowRemove) {
543 			this.array = array;
544 			this.allowRemove = allowRemove;
545 		}
546 
hasNext()547 		public boolean hasNext () {
548 			if (!valid) {
549 // System.out.println(iterable.lastAcquire);
550 				throw new GdxRuntimeException("#iterator() cannot be used nested.");
551 			}
552 			return index < array.size;
553 		}
554 
next()555 		public T next () {
556 			if (index >= array.size) throw new NoSuchElementException(String.valueOf(index));
557 			if (!valid) {
558 // System.out.println(iterable.lastAcquire);
559 				throw new GdxRuntimeException("#iterator() cannot be used nested.");
560 			}
561 			return array.items[index++];
562 		}
563 
remove()564 		public void remove () {
565 			if (!allowRemove) throw new GdxRuntimeException("Remove not allowed.");
566 			index--;
567 			array.removeIndex(index);
568 		}
569 
reset()570 		public void reset () {
571 			index = 0;
572 		}
573 
iterator()574 		public Iterator<T> iterator () {
575 			return this;
576 		}
577 	}
578 
579 	static public class ArrayIterable<T> implements Iterable<T> {
580 		private final Array<T> array;
581 		private final boolean allowRemove;
582 		private ArrayIterator iterator1, iterator2;
583 
584 // java.io.StringWriter lastAcquire = new java.io.StringWriter();
585 
ArrayIterable(Array<T> array)586 		public ArrayIterable (Array<T> array) {
587 			this(array, true);
588 		}
589 
ArrayIterable(Array<T> array, boolean allowRemove)590 		public ArrayIterable (Array<T> array, boolean allowRemove) {
591 			this.array = array;
592 			this.allowRemove = allowRemove;
593 		}
594 
iterator()595 		public Iterator<T> iterator () {
596 // lastAcquire.getBuffer().setLength(0);
597 // new Throwable().printStackTrace(new java.io.PrintWriter(lastAcquire));
598 			if (iterator1 == null) {
599 				iterator1 = new ArrayIterator(array, allowRemove);
600 				iterator2 = new ArrayIterator(array, allowRemove);
601 // iterator1.iterable = this;
602 // iterator2.iterable = this;
603 			}
604 			if (!iterator1.valid) {
605 				iterator1.index = 0;
606 				iterator1.valid = true;
607 				iterator2.valid = false;
608 				return iterator1;
609 			}
610 			iterator2.index = 0;
611 			iterator2.valid = true;
612 			iterator1.valid = false;
613 			return iterator2;
614 		}
615 	}
616 }
617