6

I get data in the format of an ObservableMap<T, Foo>. To implement this data in a View I need the values of that map in the format of an ObservableList<Foo>.

Because I need the data in a Map Collection (to avoid duplicates and other reasons) I was wondering if it is possible to bind these 2 collections.

Something like:

ObservableMap<Integer, Foo> data = FXCollections.observableHashMap();
ObservableList<Foo> target = FXCollections.observableArrayList();

// Won't work 
Bindings.bindContent(target, data.values());

Binding because the data can change during runtime.

Edit: Initializing the List with map values is not working because the there will be addtional Foo's added to the map: Example with a String as map value:

ObservableMap<Integer, String> map = FXCollections.observableHashMap();
map.put(1, "a");
map.put(2, "b");

ObservableList<String> list = FXCollections.observableArrayList(map.value());

//map = {1=a, 2=b}
//list = [a, b]
// ok

map.put(3, "c");

//map = {1=a, 2=b, 3=c}
//list = [a, b]
// no update    

2 Answers2

1

from ObservableMap to ObservableList

ObservableMap<Integer, Foo> data = FXCollections.observableHashMap();
ObservableList<Foo> target = FXCollections.observableArrayList(data.values());
rohit prakash
  • 565
  • 6
  • 12
  • 2
    i want to keep target updated. once initialized with data.values() no changes in data are recognized. – Thomas Bernhard Aug 20 '19 at 13:11
  • 1
    no, as the api doc of observableArrayList(Collection) clearly states _Creates a new observable array list and adds a content of collection col to it._ – kleopatra Aug 20 '19 at 15:21
  • @kleopatra Yeah but he want to bind them, and after adding new values to the map put it automatically in list without additional updating it again, so your solution won't work. – SURU Aug 22 '19 at 05:49
  • @SURU a) never (yet, maybe I'll do later) posted a _solution_. If you mean my comment/s: b) manual listeners will always work c) I was wrong in suggesting a custom transformList (requires the backing source to be a list) - a custom ObservableList backed by an ObservableMap would do, though – kleopatra Aug 22 '19 at 11:37
1

I have not found a built in solution in Java. I use a simple MapChangeListener when the Map is the source of the data, and the List is only for tracking changes to the Map value list.

    ObservableMap<String, String> map = FXCollections.observableHashMap();
    ObservableList<String> list = FXCollections.observableArrayList();

    MapChangeListener<String, String> listener = c -> {
        if (c.wasRemoved()) {
            list.remove(c.getValueRemoved());
        }

        if (c.wasAdded()) {
            list.add(c.getValueAdded());
        }
    };

    map.addListener(listener);

    map.put("1", "A");
    map.put("2", "B");
    map.put("1", "A'");

    assertThat(list).containsExactlyInAnyOrder("A'", "B");

You should keep the observable list private, and only expose a unmodifiableObservableList externally, to prevent accidently adding list entries that are not in the map.

NaderNader
  • 1,038
  • 1
  • 11
  • 16
  • I created a utility class for this. That way it is easy to reuse the code. You can find the Gist [here](https://gist.github.com/aron-hoogeveen/757c71edddfa18c207870a3e62e28571) – Aron Hoogeveen Apr 03 '21 at 15:59