7

I want to create my own implementation of ArrayList in java, that can listen when the list is changing and to do action when this happens. From what I have read, I understand that I can't extend ArrayList and then add listener.

I want to use MyList in class as a variable with public modifier, so users can change it directly and to be done action when he changes it.

class MyList extends ArrayList<object>.... {  ... }
 class UseOfMyList {
 public MyList places = new MyList<Object>();
 places.add("Buenos Aires");
 //and to be able to do that
 List cities = new ArrayList<Object>();
 cities.add("Belmopan");
 places = cities;

So how to create and when do add,remove or pass another list to MyList an action to be performed?

Xelian
  • 16,680
  • 25
  • 99
  • 152
  • What is your question? – Daniel Kaplan May 13 '13 at 19:07
  • You can extend `ArrayList` class since is not marked as `final`. But what's the purpose of having a listener in it? It would be better to have a class that uses a `List` backed up by an `ArrayList` and in its `add` method has a listener to notify others that the inner list has changed. – Luiggi Mendoza May 13 '13 at 19:10
  • I don't say that I want listener in it, I want to do an action when MyList changes, I don't know how to implement it. – Xelian May 13 '13 at 19:13
  • Similar: http://stackoverflow.com/questions/6578308/add-listener-to-arraylist – Joshua Goldberg Jan 09 '14 at 16:05
  • I provided a solution in [http://stackoverflow.com/questions/6578308/add-listener-to-arraylist][1] [1]: http://stackoverflow.com/questions/6578308/add-listener-to-arraylist – Michael Baldauf Aug 26 '15 at 09:45

3 Answers3

13

You're not going to be able to do this by extending ArrayList, as it has no built-in notification mechanism (and, further, because it is has been declared final and thus cannot be extended). However, you can achieve your desired result by creating your own List implementation and adding your "listener" functionality vis a vis the add() and remove() methods:

class MyList<T>{
    private ArrayList<T> list;

    public MyList(){
        list = new ArrayList<>();
        ...
    }
    public void add(T t){
        list.add(t) 
        //do other things you want to do when items are added 
    }
    public T remove(T t){
        list.remove(t);
        //do other things you want to do when items are removed
    }
}
Seyit Bilal
  • 191
  • 2
  • 16
drew moore
  • 31,565
  • 17
  • 75
  • 112
  • Ok, but I want to catch the case : `MyList list = new MyList(); list.add("one") MyList list2 = new MyList(); list2 = list1` and when this happens to do action too. – Xelian May 13 '13 at 19:31
  • I'm not quite sure what you mean... It seems like you want to do something whenever a `MyList<>` is initialized? If so, just add that functionality to the constructor. If not, please edit your question with a full description of what you need to do. – drew moore May 13 '13 at 19:38
  • I want to do something not when the list is initializing but when make referance of the value of one variable to another. `list = list1` Here the constructor is not used(or I am wrong). But the values of the **list ** has changed. – Xelian May 13 '13 at 20:01
  • I am also not sure what you want. Do you want a List implementation that fires change notifications when elements are added or removed? Or do you want a List of elements that fire change notifications when they themselves are changed? – scottb May 13 '13 at 20:10
  • 1
    You can receive change notifications by using ObservableValues as your list elements. With them, you can use the Property Binding API to attach ChangeListeners to them. You don't need to write any new List implementation to work with ObservableValues. If you want a List that will respond to changes in its structure, then you'll need to write your own List implementation as has already been suggested. Have you looked at FXCollections.observableArrayList? – scottb May 13 '13 at 20:28
  • ^ what he said... let me know if I can explain any of that further though – drew moore May 14 '13 at 11:08
  • I can't explain properly. Let's try again. I want to do something when change the reference of the variable from my class. `list = list1` For my case, elements in list are changed and want to do something. – Xelian May 14 '13 at 15:32
  • `ArrayList` is `final`? – user1803551 May 30 '16 at 14:37
3

Old question, I know. I apologize in advance for any bad formatting or missing lines of code. I'm a long-time user, first time contributor.

Anyhow, because of the removal of JavaFX from the JDK11, I was forced to write my own version of the ObservableList. Sure, we can plop JavaFX in with JMods or Maven, but it seems like a bit of an overkill just for the FXCollections.

Long Story made Short...er :)

I started out reading this old question and the answer didn't suit my needs fully, so I've added a custom event/listener class.

Figured I could share since this site has improved my coding 10 fold.

public static void main(String[] args) {        
    BackedList<String> list = new BackedList();
    list.addListener(new BackedListListener<String>(){
        @Override
        public void setOnChanged(ListChangeEvent<String> event) {
            if (event.wasAdded()) {
                event.getChangeList().forEach(e->{
                   // do whatever you need to do                        
                    System.out.println("added: " + e);
                });
            }
            if (event.wasRemoved()) {

                // do whatever you need to dl
                event.getChangeList().forEach(e->{System.out.println(e + " was removed");});
            }
        }
    });

Class: BackedObservableList

    public class BackedObservableList<T> implements List<T> {
        private final List<T> backed;

    public BackedObservableList() {
        backed = new ArrayList();
    }

    public BackedObservableList(List<T> backed) {
        this.backed = backed;
    }

        /*

        You will want to override every method. For any method that performs an add/remove 
        operation, you will have to do some coding / testing. I'll do an add() op, a remove() 
        op, and an interator in this example. Anything that is not an add/remove op, you can straight up delegate it to the underlying list. 
Also remember that list.clear() is a removal operation, where you can simply iterate through the backed list and call the overide remove(T t) method, or just plop the whole backed list into the ListChangeEvent<T> class and delegate to the backed array again.


                */


     @Override
        public boolean add(T e) {
            if (backed.add(e)) {
                ListChangeEvent<T> event = new ListChangeEvent(this, backed.indexOf(e), backed.indexOf(e) + 1, true, e);
                    notifyListeners(event);
                return true;
                }
            return false;
                }

        }

    @Override
    public boolean remove(Object o) {
        if (backed.remove(o)) {
            ListChangeEvent<T> event = new ListChangeEvent(this, backed.indexOf(o), 

    backed.indexOf(o) + 1, false, o);
                notifyListeners(event);
                return true;
            }
            return false;
        }
    /*

    The iterator seemed easy enough, until I remembered the iterator.remove() call. 
    I still haven't fully tested it (it works, but only as far as I've used it)

    */
     @Override
        public Iterator<T> iterator() {        
            return new Iterator<T>() {
                T currentItem = null;
                int currentIndex = 0;

            @Override
            public boolean hasNext() {
                return backed.size() > currentIndex;
            }

            @Override
            public T next() {

                return currentItem = backed.get(currentIndex++);
            }

            @Override
            public void remove() {
                if (backed.remove(currentItem)) {
                    currentIndex--;
                    notifyListeners(new ListChangeEvent<T>(backed, currentIndex, currentIndex + 1, false, currentItem));
                }
            }
        };
    }


     private void notifyListeners(ListChangeEvent<T> event) {
            for (BackedListListener<T> listener : listeners) {
                listener.setOnChanged(event);
            }
        }

        private final List<BackedListListener> listeners = new ArrayList();

        public void addListener(BackedListListener<T> listener) {
            listeners.add(listener);
        }

Class: ListChangeEvent

It simply provides a reference to the backed list (which you may want to wrap with Collections.unmodifiableList()

public class ListChangeEvent<T> {
private final List<T> source;
private final List<T> changeList;
private final boolean wasAdded;    
private final int to, from;

public ListChangeEvent(List<T> source, int from, int to, boolean wasAdded, T... changeItems) {
    this(source, from, to, wasAdded, Arrays.asList(changeItems));
}


    public ListChangeEvent(List<T> source, int from, int to, boolean wasAdded, List<T> changeItems) {
        this.source = source;
        this.changeList = changeItems;
        this.wasAdded = wasAdded;
        this.to = to;
        this.from = from;                
    }

    public int getFrom() {
        return from;
    }

    public int getTo() {
        return to;
    }

    public List<T> getSource() {
        return source;
    }

    public List<T> getChangeList() {
        return changeList;
    }

    public boolean wasAdded() {
        return wasAdded;
    }

    public boolean wasRemoved() {
        return !wasAdded;
    }            
}

Class: BackedListListener

    /*
    Finally a little functional interface... or, because I was too lazy to change it to one, a simple one-liner abstract class with some generics
    */
    public abstract class BackedListListener<T> {

    public abstract void setOnChanged(ListChangeEvent<T> event);        

}
Nouman Ch
  • 4,023
  • 4
  • 29
  • 42
Benson99
  • 31
  • 1
-1

the resp. ;)

private class MyList extends ArrayList<Objects> {

      @Override
      public void sort(Comparator c) {
        super.sort(c); 
        resetLancamentos(); // call some metod ;)
      }
    //...
     @Override
     public boolean removeAll(Collection c) {
        //To change body of generated methods, choose Tools | Templates.
        boolean ret = super.removeAll(c);
        resetLancamentos(); // some metod like fireObjChanged() will do the job too
         return ret;
     }

}
AzzArr
  • 1
  • 2