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 understan
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 list = new BackedList();
list.addListener(new BackedListListener(){
@Override
public void setOnChanged(ListChangeEvent 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 implements List {
private final List backed;
public BackedObservableList() {
backed = new ArrayList();
}
public BackedObservableList(List 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 class and delegate to the backed array again.
*/
@Override
public boolean add(T e) {
if (backed.add(e)) {
ListChangeEvent 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 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 iterator() {
return new Iterator() {
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(backed, currentIndex, currentIndex + 1, false, currentItem));
}
}
};
}
private void notifyListeners(ListChangeEvent event) {
for (BackedListListener listener : listeners) {
listener.setOnChanged(event);
}
}
private final List listeners = new ArrayList();
public void addListener(BackedListListener 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 {
private final List source;
private final List changeList;
private final boolean wasAdded;
private final int to, from;
public ListChangeEvent(List source, int from, int to, boolean wasAdded, T... changeItems) {
this(source, from, to, wasAdded, Arrays.asList(changeItems));
}
public ListChangeEvent(List source, int from, int to, boolean wasAdded, List 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 getSource() {
return source;
}
public List 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 {
public abstract void setOnChanged(ListChangeEvent event);
}