Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to convert an observableset to an observablelist

I am trying to set items to a tableview but the setitems method expects an observablelist while I have an observableset in my model.The FXCollections utility class does not have a method for creating an observable list given an observable set.I tried casting but that caused a class cast exception (as expected).

Currently I am using this kind of code

new ObservableListWrapper<E>(new ArrayList<E>(pojo.getObservableSet()));

And I have some problems with it:

  • Will editing this in the table update the underlying set as expected?
  • Is it the 'right' way of doing this

So in short I need a style guide or best practice for converting between observable set and observable list because I expect to be doing this a lot when building a java fx GUI

like image 334
Japheth Ongeri - inkalimeva Avatar asked Jul 17 '14 15:07

Japheth Ongeri - inkalimeva


People also ask

What is FXCollections?

public class FXCollections extends Object. Utility class that consists of static methods that are 1:1 copies of java. util. Collections methods.

What is Observablearraylist?

ArrayList: Resizable-array implementation of the List interface. Implements all optional list operations, and permits all elements, including null. ObservableList: A list that allows listeners to track changes when they occur.


2 Answers

Will editing this in the table update the underlying set as expected ?

No because, you are doing a copy of the set:

new ArrayList<E>(pojo.getObservableSet())

Is it the 'right' way of doing this ?

I think the right way is not doing that. Set are not List and vice versa. Both have specific contraints. For example, the lists are ordered and sets contains no duplicate elements.

Moreover, nor FXCollections neither Bindings provides this kind of stuff.

I would like the collection to remain as a set to enforce uniqueness

I guess you could write a custom ObservableList, for example the Parent::children have a similar behavior. It throws an IllegalArgumentException if a duplicate children is added. If you look at the source code, you will see that it is a VetoableListDecorator extension. You could write your own:

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

import com.sun.javafx.collections.VetoableListDecorator;

public class CustomObservableList<E> extends VetoableListDecorator<E> {

    public CustomObservableList(ObservableList<E> decorated) {
        super(decorated);
    }

    @Override
    protected void onProposedChange(List<E> toBeAdded, int... indexes) {
        for (E e : toBeAdded) {
            if (contains(e)) {
                throw new IllegalArgumentException("Duplicament element added");
            }
        }
    }
}

class Test {
    public static void main(String[] args) {
        Object o1 = new Object();
        Object o2 = new Object();

        Set<Object> set = new HashSet<Object>();
        set.add(o1);
        CustomObservableList<Object> list = new CustomObservableList<Object>(FXCollections.observableArrayList(set));
        list.add(o2);
        list.add(o1); // throw Exception
    }
}
like image 153
gontard Avatar answered Nov 09 '22 05:11

gontard


Just in Case someone stumbles over this question looking for a one-way to convert an ObservableSet into an ObservableList... I post my solution. It doesn't support feeding back data to the set (which in my opinion wouldn't be nice since TableView doesn't have a concept of not being able to change a value) but supports updates of the set and preserves the (in this case) sorted order.

package de.fluxparticle.lab;

import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.ObservableSet;
import javafx.collections.SetChangeListener;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.stage.Stage;
import javafx.util.Duration;

import java.util.Collections;
import java.util.Random;
import java.util.TreeSet;

import static javafx.collections.FXCollections.observableSet;

/**
 * Created by sreinck on 23.01.17.
 */
public class Set2List extends Application {

    private final ObservableSet<Integer> setModel = observableSet(new TreeSet<Integer>());

    @Override
    public void start(Stage primaryStage) throws Exception {
        TableView<Integer> tableView = new TableView<>();
        addColumn(tableView, "Number");

        ObservableList<Integer> list = convertSetToList(setModel);
        tableView.setItems(list);

        Random rnd = new Random();
        scheduleTask(Duration.millis(1000), () -> setModel.add(rnd.nextInt(10)));

        primaryStage.setScene(new Scene(tableView, 800, 600));
        primaryStage.setTitle("Set2List");
        primaryStage.show();
    }

    private static void scheduleTask(Duration interval, Runnable task) {
        Timeline timeline = new Timeline(new KeyFrame(interval, event -> task.run()));
        timeline.setCycleCount(Timeline.INDEFINITE);
        timeline.play();
    }

    private static ObservableList<Integer> convertSetToList(ObservableSet<Integer> set) {
        ObservableList<Integer> list = FXCollections.observableArrayList(set);

        set.addListener((SetChangeListener<Integer>) change -> {
            if (change.wasAdded()) {
                Integer added = change.getElementAdded();
                int idx = -Collections.binarySearch(list, added)-1;
                list.add(idx, added);
            } else {
                Integer removed = change.getElementRemoved();
                int idx = Collections.binarySearch(list, removed);
                list.remove(idx);
            }
        });

        return list;
    }

    private static void addColumn(TableView<Integer> tableView, String text) {
        TableColumn<Integer, String> column = new TableColumn<>(text);
        column.setCellValueFactory(param -> new SimpleStringProperty(param.getValue().toString()));
        tableView.getColumns().add(column);
    }

    public static void main(String[] args) {
        launch(args);
    }

}    
like image 45
FLUXparticle Avatar answered Nov 09 '22 05:11

FLUXparticle