Usually, in java, to delete an item from a stack (or set) I would do something along the lines of:
Stack<Particle> particles = new Stack<Particle>();
int i = 0, ;
while(i < particles.size()) {
if(particles.elementAt(i).isAlive()) {
i ++;
} else {
particles.remove(i);
}
}
I've searched the android docs and googled quite a few times in an attempt to achieve the same results, but nothing seems to work. Can anyone help me here?
Try looping using an Iterator, since per Oracle Iterator.remove() is the only safe way
to remove an item from a Collection (including a Stack) during iteration.
From http://docs.oracle.com/javase/tutorial/collections/interfaces/collection.html
Note that Iterator.remove is the only safe way to modify a collection during iteration; the behavior is unspecified if the underlying collection is modified in any other way while the iteration is in progress.
So something like the following should work:
Stack<Particle> particles = new Stack<Particle>();
... // Add a bunch of particles
Iterator<Particle> iter = particles.iterator();
while (iter.hasNext()) {
Particle p = iter.next();
if (!p.isAlive()) {
iter.remove();
}
}
I've used this approach in a real Android app (OneBusAway Android - see code here), and it worked for me. Note that in the code for this app I also included a try/catch block in case the platform throws an exception, and in this case just iterate through a copy of the collection and then remove the item from the original collection.
For you, this would look like:
try {
... // above code using iterator.remove
} catch(UnsupportedOperationException e) {
Log.w(TAG, "Problem removing from stack using iterator: " + e);
// The platform apparently didn't like the efficient way to do this, so we'll just
// loop through a copy and remove what we don't want from the original
ArrayList<Particle> copy = new ArrayList<Particle>(particles);
for (Particle p : copy) {
if (!p.isAlive()) {
particles.remove(p);
}
}
}
This way you get the more efficient approach if the platform supports it, and if not you still have a backup.
Have you ever try this:
Stack<String> stack = new Stack<String>();
stack.push("S");
stack.push("d");
for (String s : stack){
stack.pop();
}
Related
In the Head First Design Patterns book, the authors describe using an iterator to traverse over composite data structures. They provide some sample code which, when executed, prints out a series of menu items stored within the composite. However, if you try to call the iterator more than once, it no longer works as expected and won't produce any results. The following code appears to be causing the problem:
public Iterator<MenuComponent> createIterator() {
if (iterator == null) {
iterator = new CompositeIterator(menuComponents.iterator());
}
return iterator;
}
In essence, they are creating a singleton iterator that cannot be reset for future iterations. Unfortunately, simply replacing this logic to return a new instance of the CompositeIterator also breaks the algorithm. An issue was raised on GitHub several years ago, although is yet to be resolved. Does anyone have any suggestions on how to overcome this issue?
As the linked issue says in the comments:
return iterator; // the `iterator' never resets to null once it's set.
We need to reset the iterator we are done with it, but not when the iterator still has elements left, because CompositeIterator depends on that.
One way to do this is to add another condition on which iterator is reset - when the iterator has no more elements:
public Iterator<MenuComponent> createIterator() {
if (iterator == null || !iterator.hasNext()) {
iterator = new CompositeIterator(menuComponents.iterator());
}
return iterator;
}
Before I start my question, i'd like to mention that i DID read up some other topics and i tried around a bit but im just really confused atm so i figured i'd just ask.
So what i wanna do is use for each through a Set and within that for each, add elements to that set and also iterate through those.
The solution I found elsewhere was the following:
for(Object obj : new HashSet<Object>(oldSet))
I tried that, however I keep missing some of the last elements i'd like to match so im not really sure if this is the right approach in the first place?
To be specific, this is basically what my code looks like:
for(Position pos : new HashSet<Position>(oldSet){
for(Delta delta : deltas){
if(board.getTokenAt(pos.plus(delta).equals(initial){
hitList.add(pos.plus(delta);
oldSet.add(pos.plus(delta);
}
}
oldSet.remove(pos);
}
Again, I'd just like to know if my approach is wrong or there must be an error elsewhere in my code so i know what to look at.
Thanks beforehand!
You can't really add to a data structure while iterating over it, that is almost guaranteed to have unexpected results.
However, there is a simple enough solution to your issue. Just process each item recursively when you find that it needs to be added, and add it to a separate List. At the end of iteration, add everything in the List to the main Set. This avoids the issue of adding during iteration while still allowing you to to process the newly added items.
It would look something like this:
List<Position> toAdd = new LinkedList<>();
for(Position pos : oldSet){
for(Delta delta : deltas){
addIfGoodAndRecurse(pos, delta, toAdd);
}
}
And then you can use this helper method to add the item if it meets your conditions and also recursively process added items. Note you will need to change the method signature to pass in your board, initial, and hitList if they are local variables. I didn't know their types or whether they were global variables or fields, so I couldn't really add them in the example.
private void addIfGoodAndRecurse(Position pos, Delta delta, List<Position> toAdd) {
Position toCheck = pos.plus(delta);
if(board.getTokenAt(toCheck.equals(initial))) {
hitList.add(toCheck);
toAdd.add(toCheck);
for (Delta recursionDelta : deltas) {
addIfGoodAndRecurse(toCheck, recursionDelta, toAdd);
}
}
}
I don't have your code, so I can't test this. The idea should work fine, but you may need to make slight modifications.
You can iterate through new elements added to a list that you're iterating if you add them to the end of the list and iterate through it using an index and the get() method, not through an Iterator. You can also use the Set as you are doing now, but only to make sure you only add unique items to your collection.
List<Position> list = new ArrayList<>(oldSet);
for (int i = 0; i < list.length; ++i) { // NB list.length could be different each time
Position pos = list.get(i);
for(Delta delta : deltas){
if(board.getTokenAt(pos.plus(delta).equals(initial){
hitList.add(pos.plus(delta));
if (oldSet.add(pos.plus(delta))) // Check if it already exists in the list
list.add(pos.plus(delta));
}
}
oldSet.remove(pos);
}
We all know that the safest "and probably only safe" way of removing an object from a collection while iterating it, is by first retrieving the Iterator, perform a loop and remove when needed;
Iterator iter=Collection.iterator();
while(iter.hasNext()){
Object o=iter.next()
if(o.equals(what i'm looking for)){
iter.remove();
}
}
What I would like to understand, and unfortunately haven't found a deep technical explanation about, is how this removal is performed,
If:
for(Object o:myCollection().getObjects()){
if(o.equals(what i'm looking for)){
myCollection.remove(o);
}
}
Will throw a ConcurrentModificationException, what does "in technical terms" Iterator.remove() do? Does it removes the object, breaks the loop and restart the loop?
I see in the official documentation:
"Removes the current element. Throws IllegalStateException if an
attempt is made to call remove() that is not preceded by a call to
next( )."
The part "removes the current element", makes me think of the exact same situation happening in a "regular" loop => (perform equality test and remove if needed), but why is the Iterator loop ConcurrentModification-safe?
The reason why you cannot modify a list while iterating over it is because the iterator has to know what to return for hasNext() and next().
How this is done is implementation specific, but you could have a look at the source code of ArrayList/AbstractList/LinkedList etc.
Also note that in some situations you can use some code like this as an alternative:
List<Foo> copyList = new ArrayList<>(origList);
for (Foo foo : copyList){
if (condition){
origList.remove(foo);
}
}
But this code will probably run slightly slower because the collection has to be copied (shallow copy only) and the element to remove has to be searched.
Also note that if you're using the iterator directly it's recommended to use a for loop instead of while loop as this limits the scope of the variable:
for (Iterator<Foo> iterator = myCollection.iterator(); iterator.hasNext();){
...
}
How exactly Iterator removes elements depends on its implementation, which may be different for different Collections. Definitely it doesn't break the loop you're in. I've just looked how ArrayList iterator is implemented and here's the code:
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
So it checks for concurrent modifications, removes element using public ArrayList remove method, and increments counter of list modifications so ConcurrentModificationException won't be thrown at next iteration.
I am currently trying to save special Actors so i can put them on a map again if the old map get loaded. Therefor i want to put them into a HashMap<String, ArrayList<Monster>> monsterAtMap and remove them from there Stages. So i am trying this:
private void saveMonsters() {
if (this.screen.figureStage.getActors().size == 0)
return;
ArrayList<Monster> monsters = new ArrayList<Monster>();
for (Actor a : this.screen.figureStage.getActors()) {
a.remove();
}
Gdx.app.log("Figurstage size", ""+ this.screen.figureStage.getActors().size);
this.monsterAtMap.put(this.currentMap.name, monsters);
}
As start. But i noticed that it does not delete all. It does just delete 10 thats all. I do log the size of it befor and after the deleting. It's current 21 (20Monsters and 1 Character) after delete the size is 11.I also added this this.screen.figureStage.getRoot().removeActor(a); but this does not change anything.
Any Idea to that?
[EDIT] I wrote a workaround so my idea is working but the general idea that should work isnt possible because the .remove() does not always delete the Actor in anyway?! The workaround does look like this:
private void saveMonsters() {
this.chara = this.screen.character;
if (this.screen.figureStage.getActors().size == 0)
return;
ArrayList<Monster> monsters = new ArrayList<Monster>();
for (Actor a : this.screen.figureStage.getActors()) {
if (a.getClass() == Monster.class)
monsters.add((Monster) a);
}
this.screen.figureStage.clear();
this.screen.figureStage.addActor(chara);
this.monsterAtMap.put(this.currentMap.name, monsters);
}
The .clear()does work correct.
Deleting objects from a container while iterating over that container is always fraught with issues and complications, and I think you're running into some of these issues with the Stage's list of actors. The Stage code tries to use SnapshotArray to hide some of these issues, but its not clear to me that it will work with the code you've written.
One way to avoid this would be to loop through getActors() once and copy the actors into the monsters array, then loop through the monsters array and remove the actors from the Stage (or invoke figureStage.getRoot().clearChildren()). This should prevent you from iterating over a list that you're modifying.
Alternatively, look at how Group.clearChildren() is implemented (it uses an explicit integer index in the array of children, and not an iterator over the Array, and avoid some of the issues).
I need to update some fixed-priority elements in a PriorityQueue based on their ID. I think it's quite a common scenario, here's an example snippet (Android 2.2):
for (Entry e : mEntries) {
if (e.getId().equals(someId)) {
e.setData(newData);
}
}
I've then made Entry "immutable" (no setter methods) so that a new Entry instance is created and returned by setData(). I modified my method into this:
for (Entry e : mEntries) {
if (e.getId().equals(someId)) {
Entry newEntry = e.setData(newData);
mEntries.remove(e);
mEntries.add(newEntry);
}
}
The code seems to work fine, but someone pointed out that modifying a queue while iterating over it is a bad idea: it may throw a ConcurrentModificationException and I'd need to add the elements I want to remove to an ArrayList and remove it later. He didn't explain why, and it looks quite an overhead to me, but I couldn't find any specific explanation on internet.
(This post is similar, but there priorities can change, which is not my case)
Can anyone clarify what's wrong with my code, how should I change it and - most of all - why?
Thanks,
Rippel
PS: Some implementation details...
PriorityQueue<Entry> mEntries = new PriorityQueue<Entry>(1, Entry.EntryComparator());
with:
public static class EntryComparator implements Comparator<Entry> {
public int compare(Entry my, Entry their) {
if (my.mPriority < their.mPriority) {
return 1;
}
else if (my.mPriority > their.mPriority) {
return -1;
}
return 0;
}
}
This code is in the Java 6 implementation of PriorityQueue:
private class Itr implements Iterator<E> {
/**
* The modCount value that the iterator believes that the backing
* Queue should have. If this expectation is violated, the iterator
* has detected concurrent modification.
*/
private int expectedModCount = modCount;
public E next() {
if(expectedModCount != modCount) {
throw new ConcurrentModificationException();
}
}
}
Now, why is this code here? If you look at the Javadoc for ConcurrentModificationException you will find that the behaviour of an iterator is undefined if modification occurs to the underlying collection before iteration completes. As such, many of the collections implement this modCount mechanism.
To fix your code
You need to ensure that you don't modify the code mid-loop. If your code is single threaded (as it appears to be) then you can simply do as your coworker suggested and copy it into a list for later inclusion. Also, the use of the Iterator.remove() method is documented to prevent ConcurrentModificationExceptions. An example:
List<Entry> toAdd = new ArrayList<Entry>();
Iterator it = mEntries.iterator();
while(it.hasNext()) {
Entry e = it.next();
if(e.getId().equals(someId)) {
Entry newEntry = e.setData(newData);
it.remove();
toAdd.add(newEntry);
}
}
mEntries.addAll(toAdd);
The Javadoc for PriorityQueue says explicitly:
"Note that this implementation is not synchronized. Multiple threads should not access a PriorityQueue instance concurrently if any of the threads modifies the list structurally. Instead, use the thread-safe PriorityBlockingQueue class."
This seems to be your case.
What's wrong in your code was already explained -- implementing iterator, which can consistently iterate through collection with intersected modification is rather hard task to do. You need to specify how to deal with removed items (will it be seen through iterator?), added items, modified items... Even if you can do it consistently it will be rather complex and unefficient implementation -- and, mostly, not very usefull, since use case "iterate without modifications" is much more common. So, java architects choose to deny modification while iterate, and most collections from Java collection API follow this, and throw ConcurrentModificationException if such modification detected.
As for your code -- for me, your just should not make items immutable. Immutability is great thing, but it should not be overused. If Entry object you use here is some kind of domain object, and you really want them to be immutable -- you can just create some kind of temporary data holder (MutableEntry) object, use it inside your algorithm, and copy data to Entry before return. From my point of view it will be best solution.
a slightly better implementation is
List<Entry> toAdd = new ArrayList<Entry>();
for (Iterator<Entry> it= mEntries.iterator();it.hasNext();) {
Entry e = it.next();
if (e.getId().equals(someId)) {
Entry newEntry = e.setData(newData);
it.remove();
toAdd.add(newEntry);
}
}
mEntries.addAll(toAdd);
this uses the remove of the iterator and a bulk add afterwards