Reputation: 21
I have a program where I am creating an arrayList
. One method randomly removes an item from the list. I also have a method that resizes the array if a certain amount of elements are added to make space for more elements to be added if wanted.
I am receiving a NullPointerExceptionError
when the lists are resized and then the method tries to remove one of the null
spaces in the newly resized array.
Without using a for loop and keeping a constant time, I'm trying to find a way to make my int
value of choice
just the number of non null
elements in the array. I hope that makes sense - can anyone help?
remove
method:
public T remove() {
if (isEmpty()) {
return null;
}
Random rand = new Random();
int choice = rand.nextInt(size);
size--;
elements[choice] = elements[size];
elements[size] = null;
return elements[choice];
}
Upvotes: 1
Views: 71
Reputation: 5093
This question boils down to "Given an array with null elements scattered throughout, how can I return the nth non-null element?" The answer is iterate through the array, which doesn't uphold the constant time constraint your're looking for.
But wait, how can array lists work in that case?!?
Here is how java.util.ArrayList
does it:
@Override
public E get(int location) {
if (0 <= location && location < (lastIndex - firstIndex)) {
return (E) array[firstIndex + location];
}
throw new IndexOutOfBoundsException(Messages.getString("luni.0A",
location, lastIndex - firstIndex));
}
Note that there are no null checks, the Arraylist always maintains its elements in a contiguous block so gets are super fast. So some magic must happen when removing elements so there won't be nulls scattered about...
@Override
public E remove(int location) {
E result;
int localLastIndex = lastIndex;
int size = localLastIndex - firstIndex;
if (0 <= location && location < size) {
if (location == size - 1) {
result = (E) array[--localLastIndex];
array[localLastIndex] = null;
} else if (location == 0) {
result = (E) array[firstIndex];
array[firstIndex++] = null;
} else {
int elementIndex = firstIndex + location;
result = (E) array[elementIndex];
if (location < size / 2) {
System.arraycopy(array, firstIndex, array, firstIndex + 1,
location);
array[firstIndex++] = null;
} else {
System.arraycopy(array, elementIndex + 1, array,
elementIndex, size - location - 1);
array[--localLastIndex] = null;
}
}
if (firstIndex == localLastIndex) {
firstIndex = localLastIndex = 0;
}
} else {
throw new IndexOutOfBoundsException(Messages.getString("luni.0A",
location, localLastIndex - firstIndex));
}
lastIndex = localLastIndex;
modCount++;
return result;
}
There is the key! When removing an element the JDK impl doesn't leave a gap, nor does it move all the elements over to close the gap. It calls a native method that copies a chunk of array around as an atomic operation: https://docs.oracle.com/javase/7/docs/api/java/lang/System.html#arraycopy(java.lang.Object,%20int,%20java.lang.Object,%20int,%20int)
Upvotes: 2