简体   繁体   中英

ArrayList.remove(Object) throws an ArrayIndexOutOfBoundsException: -1

I have an observable, that notifies all of its registered observers.

public void notifyObservers(T message) {
      notifying = true;
      for (Observer<T> observer : observers)
         observer.notify(message);
      notifying= false;
   }

I also have this method in my observable class:

public void removeObserver(final Observer<T> observer){
  if (!this.observers.contains(observer))
     return;

  if (!notifying) {
     this.observers.remove(observer);
     return;
  }

  final ArrayList<Observer<T>> observerList = this.observers;
  new Thread(new Runnable() {

     public void run() {
        while (notifying);
        observerList.remove(observer);
     }
  }).start();

}

To make shure that the remove calls are not made during the notifying, i wait in an new Thread for the notifying to be finished. However, one of the added Observers is calling the removeObserver -method of the observable it is added to in its notify -method and I get an ArrayIndexOutOfBoundsException in the line of the removecall of the ArrayList:

Exception in thread "Thread-4" java.lang.ArrayIndexOutOfBoundsException: -1
    at java.util.ArrayList.fastRemove(Unknown Source)
    at java.util.ArrayList.remove(Unknown Source)
    at allgemeines.Observable$2.run(Observable.java:96)
    at java.lang.Thread.run(Unknown Source)

Why could that happen?

Instead of using the notifying boolean, why not use locking with synchronized? Something like:

public void notifyObservers(T message) {
  synchronized(observers) {
    for (Observer<T> observer : observers)
       observer.notify(message);
  }

...

public void removeObserver(final Observer<T> observer){
   synchronized(this.observers) { 

     final ArrayList<Observer<T>> observerList = this.observers;

     observerList.remove(observer);
   }
}

Your code isn't thread safe. try to lock your observers by synchronized in the remove and notify methods.

Solved it! Well, I could'nt figure out why the Exception was thrown in my example in the question above, but I found a way to rewrite the method that worked. I converted the list into an array and iterated over that array instead of the list, which is an attribute:

@SuppressWarnings("unchecked")
public synchronized void notifyObservers(T message) {
    Object[] observerArray = observers.toArray();
    for (Object observer : observerArray)
        ((Observer<T>) observer).notify(message);
}

The add and remove methods are now very simple (just delegating the method call to the ArrayList -methods:

public void addObserver(Observer<T> observer){
    this.observers.add(observer);
}

public void removeObserver(Observer<T> observer) {
    this.observers.remove(observer);
}

Would be still interesting to know why my first solution didn't work.

It is also possible to remove from the bottom up, that way you shouldn't be out of bounds. Don't know if this will help with your particular problem though.

for (int i = list.Count-1; i>-1; i--)
{
    list.RemoveAt(i);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM