Added a clone() method and implement Cloneable.
Removed whitespace from the ends of lines.
This commit is contained in:
parent
7b29c58fe0
commit
c6aac78f42
@ -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();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user