I have a pizza code that iterates through a list of objects and checks whether they are colliding or not. If one is collided with, it is removed from the ArrayList.
for (int i = 0; i < arrayList.size(); i++) {
Object c = arrayList.get(i);
if (Rect.intersects(foo.getRect(), c.getRect())) { //Android function, checks if the two rectangles are inside each other.
foo.setPosY(c.getPosY() + 11); // Always works.
foo.setVelY(bird.getVelY() * -1); // Always works.
arrayList.remove(i); // Occasionally fails under special circumcisions.
}
}
When opening the app fresh for the first time this works fine. However, if I exit with the back button and then quickly reopen the app, it will all work except occasionally, removing the object from the ArrayList wont happen.
If I close the app and then remove it from the recent apps list, it works. If I exit with the home key, or the back button and then immediately reopen, it sometimes fails to remove the object. I don't understand why this happens, as everything else still works. The two lines of code just before it function just fine. I just don't get it.
I suspect the problem is that you're skipping an element after one call to remove, because you're incrementing i and everything in the list is moving up one element. (It's not really clear what the symptoms are at the moment. If this turns out not to be the problem, it's still a problem.)
So if you call remove(2), the next element you want to look at now has index 2.
Common ways of fixing this:
Work backwards:
for (int i = arrayList.size() - 1; i >= 0; i--)
Use an iterator instead:
for (Iterator<Foo> iterator = arrayList.iterator(); iterator.hasNext(); ) {
Foo c = iterator.next();
if (...) {
iterator.remove();
}
}
Related
If have a workflow that removes elements of a List by a certain criteria. However certain items are skipped? Why is this happening?
List<Integer> listWithAge = new ArrayList<>();
int randomNumber = 100;
for (int i = 0; i < randomNumber; i++) {
listWithAge.add(i);
}
// this is my loop
for (int i = 0; i < listWithAge.size(); i++) {
System.out.println(i);
if ((listWithAge.get(i) % 3) == 2) listWithAge.remove(i);
}
Above code is my loop. I replaced my condition with something simpler. If I run this code my second loop only runs for 67 turns instead of 100.
It is problematic to iterate over a list and remove elements while iterating over it.
If you think about how the computer has to reconcile it, it makes sense...
Here's a thought experiment for you to go through.
If you have a list that is size 10 and you want to remove elements 1, 5, and 9 then you would think maybe the following would work:
List<String> listOfThings = ...some list with 10 things in it...;
list.remove(0);
list.remove(4);
list.remove(8);
However, after the first remove command, the list is only size 9.. Then after the second command, it's size has become 8. At this point, it hardly even makes sense to do list.remove(8) anymore because you're looking at an 8-element list and the largest index is 7.
You can also see now that the 2nd command didn't even remove the element now that you wanted.
If you want to keep this style of "remove as I go" syntax, the more appropriate way is to use Iterators. Here's an SO that talks about it and shows you the syntax you would need (see the question). It's easy to read up on elsewhere too.
How Iterator's remove method actually remove an object
Skipping a value would be the result of your list getting out of sync with your loop index because the list is reduced in size. This causes you to hop over some locations since the reduction in size affects future locations that have not been reached.
So the first thing you could do is simply correct the synchronization by decrementing i when you remove a value from the list. This will keep index at the same spot as the list shifts "left" caused by the removal.
for (int i = 0; i < listWithAge.size(); i++) {
if ((listWithAge.get(i) % 3) == 2) listWithAge.remove(i--);
}
The other option is to loop thru the list backwards.
for (int i = listWithAge.size()-1; i >= 0; i--) {
if ((listWithAge.get(i) % 3) == 2) {
listWithAge.remove(i);
}
}
This way, no values should be skipped since the removing of the element does affect the loop index's future positions relative to the changing size of the list.
But the best way would be to use an iterator as has already been mentioned by
Atmas
As a side note, I recommend you always use blocks {} even for single statements as I did above in the if block. It will save you some serious debugging time in the future when you decide you need to add additional statements and then wonder why things are no longer working.
And deleting like this from a list is very expensive, especially for large lists. I would suggest that if you don't have duplicate values, you use a Set. Otherwise, instead of deleting matching values, add the non-matching to a second list.
List<Integer> listWithAge = new ArrayList<>();
int randomNumber = 100;
for (int i = 0; i < randomNumber; i++) {
listWithAge.add(i);
}
// this is my loop
List<Integer> itemsToBeDeleted = new ArrayList<>();
for (int i = 0; i < listWithAge.size(); i++) {
System.out.println(i);
if ((listWithAge.get(i) % 3) == 2) {
itemsToBeDeleted.add(i);
}
//delete all outside the loop
//deleting inside the loop messes the indexing of the array
listWithAge.removeAll(itemsToBeDeleted);
I'm trying to make this app for android, but here's the problem, I fill 2 lists, then I shuffle them, then I make a for to mix them and make the "couples", but before this I made a while to see if the final list(the list with the couples), is the same size as the original list (if this is true so everything is ok, since the couples will be the same size as the people playing), ALWAYS if the app goes to the while, it crashes, only if never goes to the while, I get the couples, here's my code:
protected void onCreate(Bundle savedInstanceState) { //when the app starts
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_amigo__secreto);
spinner1 = (Spinner) findViewById(R.id.spinner);
lista = new ArrayList<String>();
AdminSQLiteOpenHelper admin = new AdminSQLiteOpenHelper(this, "amigoS", null, 1);
this.llenarLista();
this.llenarLista2();
if (lista.size() > 1){ //if there are more than 2 people do this
Collections.shuffle(lista);
Collections.shuffle(lista2);
reproductor = MediaPlayer.create(this, R.raw.cancion);
reproductor.setLooping(true);
reproductor.start();
sorteo(); //here is the problem, look
}
}
public int sorteo() {
do {
listaParejas.clear();
listaSalidos.clear();
int start2;
for (int i = 0; i < lista.size(); i++) {
start2 = random.nextInt(lista2.size() - 0) + 0;
if (lista2.get(start2).equals(lista.get(i))) { if the person who receives is equals to the one who gives, continue
continue;
} else { //here I add who gives and receives
listaParejas.add(lista.get(i) + "->" + lista2.get(start2));
listaSalidos.add(lista2.get(start2));
lista2.remove(start2); //And remove the person who receives so that he'll never get out again
}
}
} while (listaParejas.size() != lista.size());
return 0;
}
Please any advice, I don't know what to do next, I've trying and trying, but I have no idea why always in the while, the app fails... Thanks
I think your process is based on the luck of your random factor to make the process to finish. This is not ok at all, specially because you are doing it in the main thread. What I would do is:
Take the first list, make a copy and shuffle it using a random sort. See for example Collections.shuffle(list).
Take the first list and make the couples using the following logic:
Take the first element of the first list and couple it with the first element of the second list.
If the element from the first list is the same as the element in the second, take the next one.
Remove the elements from both lists.
Repeat until the lists are empty.
This is also ensuring you have the same number of elements and that they are not the same. You can also use a queue or a stack to make your life easier.
I am having a weird issue with java list. Please see the code below:
for ( int i=0; i < tripList.size(); i++ ) {
ModeChoiceTrip trip = tripList.get(i);
int newUniqueId = tripListIds[trip.uniqueId];
int newLinkedId = trip.linkedId >= 0 ? tripListIds[trip.linkedId] : -1;
int jointTripNum = trip.linkedId >= 0 && trip.tourType != TourTypes.SPECIAL_EVENT ? jointTripListIds[trip.linkedId] : 0;
trip.uniqueId = newUniqueId;
trip.linkedId = newLinkedId;
trip.jointTripNum = jointTripNum;
}
In the above code, the values in tripList seem correct but after executing a few iterations (up to i = 6), the values in tripList changes for all the positions.
I cannot provide the whole source code here but showing the snippet of the code where I have an issue.
I found that there are some duplicate trips in tripList. When one of the trips is changed, the copy of it (located at different position) is also changed.
I am guessing this piece of code is executed by multiple threads, Then there is every chance that List could be modified by another thread while this loop is going on.
you could try synchronizing the loop and see if issue gets resolved.
Also, you could try using for-each loop instead of the loop with counter.
for (ModeChoiceTrip trip : tripList) {
.....
}
The issues was the duplicate values in the list. Thus, when I update a value in list the copy to that value changes as well
You set the unique id to -1. So if the trip list id comes in as -1, you grab the index like tripListIds[-1]; which might be the second to the last item in the list.
edit: As I was writing this post, I made my code simpler (lost arrays entirely) and got it working. Yet I am still not sure why this specific code won't work, so I'll keep the question.
Hello.
I am writing a small puzzle game in Java (using Eclipse 4.4.2) and stumbled upon a problem inside one of my methods. Basically - it won't complete the method, it just exits the method after the for loop is done without any warnings or errors (I'm not catching exceptions either). I hope I missed something simple..
Details:
I have a method to set the colors of an object and up to 5 other objects that are linked to it through lines. I set up the color of the main object, then find the linked objects through for-loops and in the end change their colors as well. (Double checked the code for Lines, there are simple return methods and nestA and nestB as data - no problem there). lines is an array with a length of 50, nests as its members.
Here's the code:
public void highlightNests(Nest nest) {
//setting the color of the main object (a nest).
Mappers.setColor(nest, nestHighlight);
//resetting the array. Temp solution, had a return method earlier,
//this is part of the debugging.
connectedNests = null;
connectedNests = new Nest[5];
int i = 0;
Gdx.app.log("highlightNests()", "starting the loop");
for (int j=0; j<lines.length; j++) {
if (lines[j].getNestA() == nest) {
connectedNests[i] = lines[j].getNestB();
i++;
}
if (lines[j].getNestB() == nest) {
connectedNests[i] = lines[j].getNestA();
i++;
}
}
//This is where the program exits the method. The following
//lines are not run.
Gdx.app.log("highlightNests()", "entering loop");
for (int l=0; i<connectedNests.length; l++) {
Mappers.setColor(connectedNests[l], nestHighlight);
Gdx.app.log("highlightNests", "set color");
}
}
Deleting the middle section makes the end part run, so there are no errors in the last part.
Your second loop is completely wrong, you declare the counter as l and increment another counter i, you should use l<connectedNests.length change it like this:
for (int l=0; l<connectedNests.length; l++) {
Mappers.setColor(connectedNests[l], nestHighlight);
Gdx.app.log("highlightNests", "set color");
}
And the program won't finish method and exits before the loop because, it doesn't even enter the loop as it's incorrect.
You have to use l instead of i in condition like,
for (int l=0; l<connectedNests.length; l++)...
//---------^ l not i
I have an ArrayList which holds Planes (enemies) on my android game. These planes move from one side of the screen to the other and the user has to dodge them. When a plane's x value becomes less than -50 it is removed from the ArrayList. When this happens, it causes all the planes on the screen currently to 'jump' slightly. They disappear for a few milliseconds and then are re-drawn but 2px behind where they are supposed to be.
Here is the paint method, where planes is the ArrayList
public void onDraw(){
bg1.onDraw(c);
bg2.onDraw(c);
chopper.onDraw(c);
score.onDraw(c);
// PAINTS THE PLANE OR DELETES IF OFF SCREEN
for (int i = 0; i < planes.size(); i++) {
Plane p = planes.get(i);
if(p.getX()<-50){
planes.remove(p);
}else{
p.onDraw(c);
if (p.getX() < 170) {
detectPlaneCollision(p, c);
}
}
}
}
Is there a way of fixing this? Should I use a different data structure?
Thanks
Tom
I think your problem is nothing to do with your choice of data structure, but instead because you are modifying your List while you're looping over it.
Imagine you have three planes in your list - [P1,P2,P3].
On the first iteration of your loop i is 0, you process P1
P1 has getX() < -50 so you remove it, making the list now [P2,P3]
On the next iteration of the loop i is now 1 so you process P3.
This means P2 never gets processed and so will never be drawn, making it disappear briefly.
Try using an Iterator which will allow you to safely remove items while looping over the List.
Iterator<Plane> i = planes.iterator();
while (i.hasNext()) {
Plane p = i.next();
if (p.getX() < -50) {
i.remove();
} else {
p.onDraw(c);
if (p.getX() < 170) {
detectPlaneCollision(p, c);
}
}
}
Although, since you don't care about the order in which you process your Plane obejcts you could consider storing them in a Set rather than a List. A Set doesn't have to worry about maintaining an order of items as you add and remove them.
If you do a lot of removing and adding from a list, consider using a LinkedList. An ArrayList is more suitable, if there's very little change in the dataset.
Although you must also consider the fact that getting elements from a LinkedList is somewhat slower than from an ArrayList, so keep that in mind when using a LinkedList.
I think there is a bug in that code.
What if i==0 and planes[0] gets removed?
planes[n] will move into position planes[n-1]. E.g. planes[1] will move into position planes[0].
Then i gets incremented, so i==1.
Therefore the original planes[1] (which is now planes[0]) will be skipped.