tags:

views:

329

answers:

11

I am new to java programming, been programing in php so I'm used to this type of loop:

int size = mapOverlays.size();
for(int n=1;n<size;n++)
{
    mapOverlays.remove(n);
}

So I want to remove everything but the first item, so why doesn't this work? As I get it, after removal, array keys are rearranged or not?

+8  A: 

As I get it, after removal, array keys are rearranged or not? Yes, the item which was on position 2 is on position 1 after you removed the item on position 1.

You can try this:

Object obj = mapOverlays.get(0); // remember first item
mapOverlays.clear(); // clear complete list
mapOverlays.add(obj); // add first item
Daniel Engmann
This may work but it causes the list to enter an unnecessary intermediate state. If the list is used concurrently (perhaps it's a CopyOnWriteArrayList), you should avoid this solution.
Jesse Wilson
+5  A: 

Why don't you try backwards?

int size = itemizedOverlay.size();
for(int n=size-1;n>0;n--)
{
    mapOverlays.remove(n);
}
Plamena
-1 On some List implementations this will perform *terribly*, and it will never be anywhere near optimal.
Kevin Bourrillion
@Kevin, the OP isn't using "some List." He said in no uncertain terms it's an `ArrayList`. As far as I know, this will be `O(n)` for all lists built into Java. This includes `LinkedList`.
Matthew Flaschen
+2  A: 

An ArrayList has integer indices from 0 to size() - 1. You could do:

int size = mapOverlays.size();
for(int n=1;n<size;n++)
{
    mapOverlays.remove(1);
}

This probably matches what you expect from PHP. It works by continually removing the 1th element, which changes. However, this has poor performance, since the internal array must constantly be shifted down. It is better to use clear() or go in reverse order.

It's too bad removeRange is protected, as that would be convenient for this type of operation.

Matthew Flaschen
+1 for pointing out the performance disadvantage of your potential solution. Also... remember you can always extend ArrayList and make public any methods which were protected.
Tim Bender
You don't need `removeRange` (and the issue is not that it's protected, but that it doesn't even exist on the `List` interface (and rightly so)). See Adam Crune's answer.
Kevin Bourrillion
@Kevin, the OP isn't using the `List` interface, so that's a non-sequitur. You could argue he should, but maybe he is sure he explicitly wants an array-based class for its performance characteristics.
Matthew Flaschen
+1  A: 

I'm assuming that mapOverlays holds an ArrayList reference.

If mapOverlays is declared as a List or ArrayList, then mapOverlays.remove(n) will refer to the remove(int) method that removes the object at a given offset. (So far so good ...)

When you remove the nth element of an array using remove(int), the elements starting at position n + 1 and above all get moved down by one. So what you are doing won't actually work in most cases. (In fact, you are likely to remove about half of the elements you want to remove, and then get an IndexOutOfBoundsException.)

The best solution is either:

    for (int i = size - 1; i > 0; i--) {
        mapOverlays.remove(i);
    }

or

    tmp = mapOverlays.remove(0);
    mapOverlays.clear();
    mapOverlays.add(tmp);

(Note that the first solution always removes from the end of the list, avoiding the need to copy elements to fill in the hole left by the removed element. The performance different is significant for a large ArrayList.)

However, if mapOverlays is declared as a Collection, remove(n) will bind to the remove(<E>) overload which removes the object that matches its argument. Depending on the declared type, this will either give you a compilation error, or the int will be autoboxed as an Integer and you (probably) won't remove anything. GOTCHA!

Stephen C
+1  A: 

Simple.

mapOverlays = Collections.singletonList(mapOverlays.get(0));
Jim
Maybe not what the OP wants - the resulting list has nothing but the first element but you can't add anything to it anymore. So it's not a wrong answer but it introduces some limitations.
Andreas_D
+4  A: 

I think it would be faster to create a new ArrayList with just the first element inside. something like :

E temp = mapOverlays.get(0);
mapOverlays = new ArrayList<E>().add(temp);
Agemen
Btw, Daniel's proposal is maybe better, because it doesn't create a new object. So references to the object stay.
Agemen
A: 

If you are using a java.util.List implementation instead of array, the size of the array gets smaller everytime you remove something and the n+1 item replaces the n item. This code will eventually result to ArrayIndecOutOfBoundsException when n becomes greated than the last index in the list.

Java also has an array type and the size of that one cannot be changed:

Object[] mapOverlay = //initialize the array here
int size = mapOverlay.length;
for(int n=1;n<size;n++)
{
    mapOverlay[n] = null;
}

I don't know PHP but this sounds like it's close to the behavior you are after. However the List implementations are more flexible and comfortable in use than the arrays.

EDIT: Here's a link to the Javadoc of List.remove(int): http://java.sun.com/javase/6/docs/api/java/util/List.html#remove%28int%29

fish
+20  A: 

You could use

mapOverlays.subList(1, mapOverlays.size()).clear();
Adam Crume
+1 It is just unbelievable to me that this is not the highest-rated answer. It's both the simplest to code *and* likely to be the most efficient (as each List implementation will perform the task in the most optimal way given its structure).
Kevin Bourrillion
holy crap, it went from 2 votes to the highest-rated in only an hour!
Kevin Bourrillion
This is a useful way of writing it. If you actually look at the code, though, it uses the protected `removeRange`, and does essentially the exact same thing as Plamena's solution. The main difference is the number of method calls, and where the loop is. With a good JIT, I suspect they have fairly similar performance. Clearly, the big O is the same.
Matthew Flaschen
@Matthew: It depends on the List implementation. For ArrayList (in the OpenJDK sources, at least), removeRange calls System.arraycopy once and makes no other method calls. In this particular case, it moves 0 elements, so essentially all it does is set the array elements to null in a tight loop and update the size field.
Adam Crume
A: 
Sonal Patil
I think the OP wants to keep the first element in the list and remove everything else.
Carlos Heuberger
A: 

Use a while loop to remove everything after the first element:

while (mapOverlays.size() > 1) {
    mapOverlays.remove(1);
}

EDIT (see comment from Adam Crume)

If performance is a problem you should use this one

while (mapOverlays.size() > 1) {
    mapOverlays.remove(mapOverlays.size()-1);
}

even a bit micro-optimization

int last = mapOverlays.size() - 1;
while (last >= 1) {
    mapOverlays.remove(last);
    last -= 1;
}



If performance is really a problem (and the list has lots of elements), you should use the sublist solution. It's a bit harder to read but probably the fastest solution if the list instance can not be recreated (referenced elsewhere).

Carlos Heuberger
For an ArrayList, this will perform very poorly - O(n^2) time - because it has to call System.arraycopy for nearly the entire array n-1 times. Remove the last element instead of the one at index 1, and it should be much better.
Adam Crume
@Adam - correct, answer edited, and maybe the `remove(1)` solution is still worse than O(n^2) but I would prefer it for small lists for being more readable (IMO) - the question did not mention performance at all. The `sublist` solution is elegant and better (the best) but not everyone understands how `sublist` works.
Carlos Heuberger
@Carlos: I don't care if people don't use sublist. I just think that reducing runtime from O(n^2) to O(n) is worth a small sacrifice to readability. The `remove(1)` solution may work fine for small lists, but I think it's better to have code that works well for any list.
Adam Crume