|
|
|
@ -43,7 +43,7 @@ import java.util.*; |
|
|
|
|
* the list is changing.</p> |
|
|
|
|
* |
|
|
|
|
* <p>All modifications, including set() operations will cause a copy of the |
|
|
|
|
* data to be created that replaces the old version. Because this list is |
|
|
|
|
* data to be created that replaces the old version. Because this list is |
|
|
|
|
* not designed for threading concurrency it further optimizes the "many modifications" |
|
|
|
|
* case by buffering them as a normal ArrayList until the next time the contents |
|
|
|
|
* are accessed.</p> |
|
|
|
@ -63,16 +63,16 @@ import java.util.*; |
|
|
|
|
* Even after ListIterator.remove() or Iterator.remove() is called, this change |
|
|
|
|
* is not reflected in the iterator instance as it is still refering to its |
|
|
|
|
* original snapshot. |
|
|
|
|
* </ul> |
|
|
|
|
* </ul> |
|
|
|
|
* |
|
|
|
|
* @version $Revision$ |
|
|
|
|
* @author Paul Speed |
|
|
|
|
*/ |
|
|
|
|
public class SafeArrayList<E> implements List<E> { |
|
|
|
|
|
|
|
|
|
public class SafeArrayList<E> implements List<E>, Cloneable { |
|
|
|
|
|
|
|
|
|
// Implementing List directly to avoid accidentally acquiring
|
|
|
|
|
// incorrect or non-optimal behavior from AbstractList. For
|
|
|
|
|
// example, the default iterator() method will not work for
|
|
|
|
|
// example, the default iterator() method will not work for
|
|
|
|
|
// this list.
|
|
|
|
|
|
|
|
|
|
// Note: given the particular use-cases this was intended,
|
|
|
|
@ -81,30 +81,48 @@ public class SafeArrayList<E> implements List<E> { |
|
|
|
|
// SafeArrayList-specific methods could then be exposed
|
|
|
|
|
// for the classes like Node and Spatial to use to manage
|
|
|
|
|
// the list. This was the callers couldn't remove a child
|
|
|
|
|
// without it being detached properly, for example.
|
|
|
|
|
// without it being detached properly, for example.
|
|
|
|
|
|
|
|
|
|
private Class<E> elementType; |
|
|
|
|
private Class<E> elementType; |
|
|
|
|
private List<E> buffer; |
|
|
|
|
private E[] backingArray; |
|
|
|
|
private int size = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public SafeArrayList(Class<E> elementType) { |
|
|
|
|
this.elementType = elementType; |
|
|
|
|
this.elementType = elementType; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public SafeArrayList(Class<E> elementType, Collection<? extends E> c) { |
|
|
|
|
this.elementType = elementType; |
|
|
|
|
this.elementType = elementType; |
|
|
|
|
addAll(c); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public SafeArrayList<E> clone() { |
|
|
|
|
try { |
|
|
|
|
SafeArrayList<E> clone = (SafeArrayList<E>)super.clone(); |
|
|
|
|
|
|
|
|
|
// Clone whichever backing store is currently active
|
|
|
|
|
if( backingArray != null ) { |
|
|
|
|
clone.backingArray = backingArray.clone(); |
|
|
|
|
} |
|
|
|
|
if( buffer != null ) { |
|
|
|
|
clone.buffer = (List<E>)((ArrayList<E>)buffer).clone(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return clone; |
|
|
|
|
} catch( CloneNotSupportedException e ) { |
|
|
|
|
throw new AssertionError(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
protected final <T> T[] createArray(Class<T> type, int size) { |
|
|
|
|
return (T[])java.lang.reflect.Array.newInstance(type, size); |
|
|
|
|
return (T[])java.lang.reflect.Array.newInstance(type, size); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected final E[] createArray(int size) { |
|
|
|
|
return createArray(elementType, size); |
|
|
|
|
return createArray(elementType, size); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Returns a current snapshot of this List's backing array that |
|
|
|
|
* is guaranteed not to change through further List manipulation. |
|
|
|
@ -114,10 +132,10 @@ public class SafeArrayList<E> implements List<E> { |
|
|
|
|
public final E[] getArray() { |
|
|
|
|
if( backingArray != null ) |
|
|
|
|
return backingArray; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if( buffer == null ) { |
|
|
|
|
backingArray = createArray(0); |
|
|
|
|
} else { |
|
|
|
|
} else { |
|
|
|
|
// Only keep the array or the buffer but never both at
|
|
|
|
|
// the same time. 1) it saves space, 2) it keeps the rest
|
|
|
|
|
// of the code safer.
|
|
|
|
@ -126,35 +144,35 @@ public class SafeArrayList<E> implements List<E> { |
|
|
|
|
} |
|
|
|
|
return backingArray; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected final List<E> getBuffer() { |
|
|
|
|
if( buffer != null ) |
|
|
|
|
return buffer; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if( backingArray == null ) { |
|
|
|
|
buffer = new ArrayList(); |
|
|
|
|
} else { |
|
|
|
|
} else { |
|
|
|
|
// Only keep the array or the buffer but never both at
|
|
|
|
|
// the same time. 1) it saves space, 2) it keeps the rest
|
|
|
|
|
// of the code safer.
|
|
|
|
|
// of the code safer.
|
|
|
|
|
buffer = new ArrayList( Arrays.asList(backingArray) ); |
|
|
|
|
backingArray = null; |
|
|
|
|
} |
|
|
|
|
return buffer; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public final int size() { |
|
|
|
|
return size; |
|
|
|
|
return size; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public final boolean isEmpty() { |
|
|
|
|
return size == 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public boolean contains(Object o) { |
|
|
|
|
return indexOf(o) >= 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public Iterator<E> iterator() { |
|
|
|
|
return listIterator(); |
|
|
|
|
} |
|
|
|
@ -162,70 +180,70 @@ public class SafeArrayList<E> implements List<E> { |
|
|
|
|
public Object[] toArray() { |
|
|
|
|
return getArray(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public <T> T[] toArray(T[] a) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
E[] array = getArray(); |
|
|
|
|
if (a.length < array.length) { |
|
|
|
|
return (T[])Arrays.copyOf(array, array.length, a.getClass()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
System.arraycopy( array, 0, a, 0, array.length ); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (a.length > array.length) { |
|
|
|
|
a[array.length] = null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return a; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public boolean add(E e) { |
|
|
|
|
boolean result = getBuffer().add(e); |
|
|
|
|
size = getBuffer().size(); |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public boolean remove(Object o) { |
|
|
|
|
boolean result = getBuffer().remove(o); |
|
|
|
|
size = getBuffer().size(); |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public boolean containsAll(Collection<?> c) { |
|
|
|
|
return Arrays.asList(getArray()).containsAll(c); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public boolean addAll(Collection<? extends E> c) { |
|
|
|
|
boolean result = getBuffer().addAll(c); |
|
|
|
|
size = getBuffer().size(); |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public boolean addAll(int index, Collection<? extends E> c) { |
|
|
|
|
boolean result = getBuffer().addAll(index, c); |
|
|
|
|
size = getBuffer().size(); |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public boolean removeAll(Collection<?> c) { |
|
|
|
|
boolean result = getBuffer().removeAll(c); |
|
|
|
|
size = getBuffer().size(); |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public boolean retainAll(Collection<?> c) { |
|
|
|
|
boolean result = getBuffer().retainAll(c); |
|
|
|
|
size = getBuffer().size(); |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void clear() { |
|
|
|
|
getBuffer().clear(); |
|
|
|
|
size = 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public boolean equals(Object o) { |
|
|
|
|
if( o == this ) |
|
|
|
|
if( o == this ) |
|
|
|
|
return true; |
|
|
|
|
if( !(o instanceof List) ) //covers null too
|
|
|
|
|
return false; |
|
|
|
@ -240,9 +258,9 @@ public class SafeArrayList<E> implements List<E> { |
|
|
|
|
if( o1 == null || !o1.equals(o2) ) |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
return !(i1.hasNext() || !i2.hasNext()); |
|
|
|
|
return !(i1.hasNext() || !i2.hasNext()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public int hashCode() { |
|
|
|
|
// Exactly the hash code described in the List interface, basically
|
|
|
|
|
E[] array = getArray(); |
|
|
|
@ -252,30 +270,30 @@ public class SafeArrayList<E> implements List<E> { |
|
|
|
|
} |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public final E get(int index) { |
|
|
|
|
if( backingArray != null ) |
|
|
|
|
return backingArray[index]; |
|
|
|
|
if( buffer != null ) |
|
|
|
|
return buffer.get(index); |
|
|
|
|
throw new IndexOutOfBoundsException( "Index:" + index + ", Size:0" ); |
|
|
|
|
throw new IndexOutOfBoundsException( "Index:" + index + ", Size:0" ); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public E set(int index, E element) { |
|
|
|
|
return getBuffer().set(index, element); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void add(int index, E element) { |
|
|
|
|
getBuffer().add(index, element); |
|
|
|
|
size = getBuffer().size(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public E remove(int index) { |
|
|
|
|
E result = getBuffer().remove(index); |
|
|
|
|
size = getBuffer().size(); |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public int indexOf(Object o) { |
|
|
|
|
E[] array = getArray(); |
|
|
|
|
for( int i = 0; i < array.length; i++ ) { |
|
|
|
@ -289,7 +307,7 @@ public class SafeArrayList<E> implements List<E> { |
|
|
|
|
} |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public int lastIndexOf(Object o) { |
|
|
|
|
E[] array = getArray(); |
|
|
|
|
for( int i = array.length - 1; i >= 0; i-- ) { |
|
|
|
@ -303,29 +321,29 @@ public class SafeArrayList<E> implements List<E> { |
|
|
|
|
} |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public ListIterator<E> listIterator() { |
|
|
|
|
return new ArrayIterator<E>(getArray(), 0); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public ListIterator<E> listIterator(int index) { |
|
|
|
|
return new ArrayIterator<E>(getArray(), index); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public List<E> subList(int fromIndex, int toIndex) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// So far JME doesn't use subList that I can see so I'm nerfing it.
|
|
|
|
|
List<E> raw = Arrays.asList(getArray()).subList(fromIndex, toIndex); |
|
|
|
|
return Collections.unmodifiableList(raw); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public String toString() { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
E[] array = getArray(); |
|
|
|
|
if( array.length == 0 ) { |
|
|
|
|
return "[]"; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
StringBuilder sb = new StringBuilder(); |
|
|
|
|
sb.append('['); |
|
|
|
|
for( int i = 0; i < array.length; i++ ) { |
|
|
|
@ -337,63 +355,63 @@ public class SafeArrayList<E> implements List<E> { |
|
|
|
|
sb.append(']'); |
|
|
|
|
return sb.toString(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected class ArrayIterator<E> implements ListIterator<E> { |
|
|
|
|
private E[] array; |
|
|
|
|
private int next; |
|
|
|
|
private int lastReturned; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected ArrayIterator( E[] array, int index ) { |
|
|
|
|
this.array = array; |
|
|
|
|
this.next = index; |
|
|
|
|
this.lastReturned = -1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public boolean hasNext() { |
|
|
|
|
return next != array.length; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public E next() { |
|
|
|
|
if( !hasNext() ) |
|
|
|
|
throw new NoSuchElementException(); |
|
|
|
|
lastReturned = next++; |
|
|
|
|
return array[lastReturned]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public boolean hasPrevious() { |
|
|
|
|
return next != 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return next != 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public E previous() { |
|
|
|
|
if( !hasPrevious() ) |
|
|
|
|
throw new NoSuchElementException(); |
|
|
|
|
lastReturned = --next; |
|
|
|
|
return array[lastReturned]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public int nextIndex() { |
|
|
|
|
return next; |
|
|
|
|
return next; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public int previousIndex() { |
|
|
|
|
return next - 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void remove() { |
|
|
|
|
// This operation is not so easy to do but we will fake it.
|
|
|
|
|
// The issue is that the backing list could be completely
|
|
|
|
|
// different than the one this iterator is a snapshot of.
|
|
|
|
|
// We'll just remove(element) which in most cases will be
|
|
|
|
|
// We'll just remove(element) which in most cases will be
|
|
|
|
|
// correct. If the list had earlier .equals() equivalent
|
|
|
|
|
// elements then we'll remove one of those instead. Either
|
|
|
|
|
// way, none of those changes are reflected in this iterator.
|
|
|
|
|
SafeArrayList.this.remove( array[lastReturned] ); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void set(E e) { |
|
|
|
|
throw new UnsupportedOperationException(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void add(E e) { |
|
|
|
|
throw new UnsupportedOperationException(); |
|
|
|
|
} |
|
|
|
|