Java Observer Template - How to remove observers during update (notification) loop / iteration?

I am very new to java, so sorry in advance if something that I say sounds brand new, be gentle.

I implemented a basic observer pattern. Some observers should only listen to one update, and then immediately remove themselves from the list of observers / listeners. However, whenever I tried to do this, I received a known java.util.concurrentmodificationexception error.

I obviously get this error because I am changing the list while still repeating it, but I'm still not sure if this is the right solution. I am wondering if I will do it right. If so, what would be the necessary fix to make it work? And if it is not, I would like to receive suggestions for a better way to achieve what I am trying to do.

Here is my code:

public interface Listener {
    public void onValueChange(double newValue);
}   


public class Observed {
    private int value;
    List<Listener>  listeners  = new ArrayList<>();

    public void addListener(Listener toAdd) {
        listeners.add(toAdd);
    }

    public void removeListener(Listener toRemove) {
        listeners.remove(toRemove);
    }

    public void changeValue(double newValue) {
        value = newValue;
        for (Listener l : listeners) l.onValueChange(newValue);                               
    }
}


public class SomeClassA implements Listener{
    private Observed observed;

    SomeClassA(Observed observed) {
        this.observed = observed;
    }

    @Override
    public void onValueChange(double newValue) {
        System.out.println(newValue);
        observed.removeListener(this);
    }
}


public class SomeClassB implements Listener{
   @Override
    public void onValueChange(double newValue) {
        System.out.println(newValue);
    } 
}



public class ObserverTest {
    public static void main(String[] args) {
        Observed observed = new Observed();
        SomeClassA objectA = new SomeClassA(observed);
        SomeClassB objectB = new SomeClassB();

        observed.addListener(objectB);
        observed.addListener(objectA);

        observed.changeValue(4);
    }
}
+4
source share
3 answers

one way is to use fo CopyOnWriteArraylist instead of ArrayList.

CopyOnWriteArraylist is a thread-safe version of ArrayList in which all mutation operations (add, set, etc.) are implemented by creating a new copy of the base array.

,

, changeValue()

+5

. , Iterator # remove. , - . :

public void changeValue(double newValue) {
    value = newValue;
    List<Listener> copyOfListeners = new ArrayList<Listener>(listeners);
    for(Listener l : copyOfListeners) {
        l.onValueChange(newValue);
    }
}
+1

the code below works, so you can try everything that it does.

import java.util.Observable;
import java.util.Observer;
class Model extends Observable {
    public void setX(double x) {
        this.x=x;
        System.out.println("setting x to "+x);
        setChanged();
        notifyObservers();
    }
    double x;
}
class A implements Observer {
    A(Model model) {
        this.model=model;
    }
    @Override public void update(Observable arg0,Object arg1) {
        System.out.println(getClass().getName()+" "+((Model)arg0).x);
        ((Model)arg0).deleteObserver(this);
    }
    Model model;
}
class B implements Observer {
    @Override public void update(Observable arg0,Object arg1) {
        System.out.println(getClass().getName()+" "+((Model)arg0).x);
    }
}
public class So19197579 {
    public static void main(String[] arguments) {
        Model model=new Model();
        model.addObserver(new A(model));
        model.addObserver(new B());
        model.setX(4);
        model.setX(8);
    }
}
0
source

All Articles