This discussion is archived
1 Reply Latest reply: Mar 4, 2013 9:45 AM by James_D RSS

JavaFx TableView issues with big data

aakash.goyal Newbie
Currently Being Moderated
I have been using JavaFx's tableview to display a huge amount of data from the database. The table is like 150+ columns and millions of rows. I can handle the rows by getting the data in chunks and implementing paginations( [http://stackoverflow.com/questions/12068907/javafx-tableview-performance-issue] ). But the no of columns is also an area of concern. It takes a lot of time to paint the data and does not update the data when I change the values in the model( [http://stackoverflow.com/questions/11065140/javafx-2-1-tableview-refresh-items/12737209#12737209] ). I dont have a predefined data structure and so the method at [http://stackoverflow.com/questions/13873361/javafx-tableview-is-not-updating] is not useful. I moved on to use JTable inside JavaFx but JFX2.0 has removed the support for Swing components inside a JavaFX scene. So being left with tableview is there some way to add pagination to columns and rows both without overutlizing the memory ?
  • 1. Re: JavaFx TableView issues with big data
    James_D Guru
    Currently Being Moderated
    You probably want to ask yourself if you really want to do this: that kind of volume of data is generally too big for a user to comfortably manage. It's usually better to find a way to display smaller amounts of data and have controls for the user to retrieve more data from the database on demand.

    That said, here's an example of how to do what you're looking for. This just displays values of x*y+c, with x and y determined by the column and row respectively, and c being determined by the user (to show effects of changing the data).

    To get the table to update when the data changes, you need to make sure the table column sees an ObservableValue (such as a Property) which wraps the values, and that changes to the values are made through the observable value. This gets a little tricky if you don't have a data structure, but it's possible to do it with lists (or even arrays) as I did in this example. You can always define a class that makes this a little nicer for your data structure, even if you don't know the number of columns a priori.

    Here I create all the columns ahead of time and store them in a list. Then I just call table.getColumns().setAll(allColumns.subList(...)) to update the displayed columns.

    I assume you've figured out row pagination, so I just made this with 50 rows. It performs reasonably.
    import javafx.application.Application;
    import javafx.beans.binding.Bindings;
    import javafx.beans.property.IntegerProperty;
    import javafx.beans.property.SimpleIntegerProperty;
    import javafx.beans.value.ChangeListener;
    import javafx.beans.value.ObservableValue;
    import javafx.collections.FXCollections;
    import javafx.collections.ObservableList;
    import javafx.event.ActionEvent;
    import javafx.event.EventHandler;
    import javafx.scene.Scene;
    import javafx.scene.control.Button;
    import javafx.scene.control.Label;
    import javafx.scene.control.TableColumn;
    import javafx.scene.control.TableColumn.CellDataFeatures;
    import javafx.scene.control.TableView;
    import javafx.scene.control.TextField;
    import javafx.scene.layout.BorderPane;
    import javafx.scene.layout.HBox;
    import javafx.stage.Stage;
    import javafx.util.Callback;
    
    public class BigTableExample extends Application {
    
      @Override
      public void start(Stage primaryStage) {
        final BigDataModel data = new BigDataModel();
        final BorderPane root = new BorderPane();
        final TableView<ObservableList<IntegerProperty>> table = new TableView<>();
        table.setItems(data.getValues());
        final ObservableList<TableColumn<ObservableList<IntegerProperty>, Number>> allColumns;
        allColumns = FXCollections.observableArrayList();
        for (int i = 0; i < data.getNumCols(); i++) {
          allColumns.add(createTableColumn(data, i));
        }
    
        final IntegerProperty firstVisibleColumn = new SimpleIntegerProperty(0);
        final int numVisibleCols = 10;
        final IntegerProperty lastVisibleColumn = new SimpleIntegerProperty();
        lastVisibleColumn.bind(firstVisibleColumn.add(numVisibleCols));
    
        firstVisibleColumn.addListener(new ChangeListener<Number>() {
    
          @Override
          public void changed(ObservableValue<? extends Number> observable,
              Number oldValue, Number newValue) {
            int first = newValue.intValue();
            int last = Math.min(allColumns.size(), newValue.intValue()
                + numVisibleCols);
            table.getColumns().setAll(allColumns.subList(first, last));
          }
    
        });
    
        table.getColumns().addAll(allColumns.subList(0, numVisibleCols));
    
        HBox colPageControls = new HBox(5);
        Button previous = new Button("Previous");
        previous.disableProperty()
            .bind(firstVisibleColumn.lessThan(numVisibleCols));
        previous.setOnAction(new EventHandler<ActionEvent>() {
          @Override
          public void handle(ActionEvent event) {
            firstVisibleColumn.set(firstVisibleColumn.get() - numVisibleCols);
          }
        });
        Button next = new Button("Next");
        next.disableProperty().bind(
            lastVisibleColumn.greaterThanOrEqualTo(allColumns.size()));
        next.setOnAction(new EventHandler<ActionEvent>() {
          @Override
          public void handle(ActionEvent event) {
            firstVisibleColumn.set(firstVisibleColumn.get() + numVisibleCols);
          }
        });
        Label colDisplayLabel = new Label();
        colDisplayLabel.textProperty().bind(
            Bindings.format("Displaying %d to %d of %d columns",
                firstVisibleColumn.add(1), lastVisibleColumn, allColumns.size()));
        colPageControls.getChildren().addAll(previous, colDisplayLabel, next);
    
        final HBox cValControls = new HBox(5);
        final TextField cVal = new TextField("0");
        final Button update = new Button("Update");
        EventHandler<ActionEvent> updateCAction = new EventHandler<ActionEvent>() {
          @Override
          public void handle(ActionEvent event) {
            try {
              int newC = Integer.parseInt(cVal.getText());
              data.setC(newC);
            } catch (NumberFormatException nfe) {
              cVal.setText(Integer.toString(data.getC()));
            }
          }
        };
        cVal.setOnAction(updateCAction);
        update.setOnAction(updateCAction);
        cValControls.getChildren().addAll(new Label("c:"), cVal, update);
    
        root.setTop(colPageControls);
        root.setCenter(table);
        root.setBottom(cValControls);
        Scene scene = new Scene(root, 800, 600);
        primaryStage.setScene(scene);
        primaryStage.setTitle("x * y + c");
        primaryStage.show();
      }
    
      private TableColumn<ObservableList<IntegerProperty>, Number> createTableColumn(
          final BigDataModel data, final int colIndex) {
        TableColumn<ObservableList<IntegerProperty>, Number> col = new TableColumn<>(
            Integer.toString(colIndex));
        col.setCellValueFactory(new Callback<CellDataFeatures<ObservableList<IntegerProperty>, Number>, ObservableValue<Number>>() {
          @Override
          public ObservableValue<Number> call(
              CellDataFeatures<ObservableList<IntegerProperty>, Number> cellData) {
            return cellData.getValue().get(colIndex);
          }
        });
        return col;
      }
    
      public static void main(String[] args) {
        launch(args);
      }
    
      public static class BigDataModel {
    
        // holds values of x^2+y^2+c for values of x and y
    
        private static final int NUM_ROWS = 50;
        private static final int NUM_COLS = 150;
    
        private final IntegerProperty c;
        private final ObservableList<ObservableList<IntegerProperty>> values;
    
        public BigDataModel() {
          this.c = new SimpleIntegerProperty(this, "c", 0);
          values = FXCollections.observableArrayList();
          for (int y = 0; y < NUM_ROWS; y++) {
            ObservableList<IntegerProperty> row = FXCollections
                .observableArrayList();
            for (int x = 0; x < NUM_COLS; x++) {
              IntegerProperty value = new SimpleIntegerProperty();
              value.bind(c.add(x * y));
              row.add(value);
            }
            values.add(row);
          }
        }
    
        public int getNumRows() {
          return NUM_ROWS;
        }
    
        public int getNumCols() {
          return NUM_COLS;
        }
    
        public int getC() {
          return c.get();
        }
    
        public void setC(int c) {
          this.c.set(c);
        }
    
        public IntegerProperty cProperty() {
          return c;
        }
    
        public ObservableList<ObservableList<IntegerProperty>> getValues() {
          return values;
        }
      }
    }

Legend

  • Correct Answers - 10 points
  • Helpful Answers - 5 points