This discussion is archived
1 2 3 Previous Next 32 Replies Latest reply: Jun 18, 2013 7:19 PM by b.pradeep RSS

TableView sorting my clicking column Header

b.pradeep Newbie
Currently Being Moderated
I am new to javaFx and is workig on TableView
my application needs sorting feature which is provided by sortorder(true) but I have data set of around 100,000 rows (10 columns each of strings of length 30),
Sorting works way better with swing Jtable

Any suggestion on how to improve sorting performance.

P.S. : I have one thing in my mind that to use some other sorting method and then load table again with sorted data but it is not a proper solution as then data is needed to be loaded again and again in tableview.

Thanks
  • 1. Re: TableView sorting my clicking column Header
    jsmith Guru
    Currently Being Moderated
    This question is cross posted to:
    http://stackoverflow.com/questions/16805845/javafx-tableview-sort-is-really-slow-how-to-improve-sort-speed-as-in-java-swing
  • 2. Re: TableView sorting my clicking column Header
    b.pradeep Newbie
    Currently Being Moderated
    I posted it there but didn't get solution
  • 3. Re: TableView sorting my clicking column Header
    James_D Guru
    Currently Being Moderated
    I think you got about as good an answer as can be given right now. Sorting appears to be considerably slower than sorting the underlying lists: that's a bug and you should report it.

    There's currently very little API allowing you to change the implementation of the sorting that is used. In JavaFX8, TableView introduces a sort() method. I haven't investigated this much, but if you're in a situation where you can use the early-access version of JDK8 you could consider subclassing TableView and overriding the sort() method.
    Have a look through the existing bug reports, particularly [url https://javafx-jira.kenai.com/browse/RT-19479]RT-19479 and related issues.

    Update: I played with overriding TableView.sort and it seems to work as expected. See [url http://stackoverflow.com/questions/16805845/javafx-tableview-sort-is-really-slow-how-to-improve-sort-speed-as-in-java-swing]cross-posted question.

    Update (again): I figured out how to fix this in JavaFX 2.2. The issue lies with getting the correct data model.

    Edited by: James_D on May 30, 2013 7:07 AM

    Edited by: James_D on May 30, 2013 12:16 PM
  • 4. Re: TableView sorting my clicking column Header
    James_D Guru
    Currently Being Moderated
    Ah, figured it out. I updated my StackOverflow post accordingly.
  • 5. Re: TableView sorting my clicking column Header
    jsmith Guru
    Currently Being Moderated
    That's a really neat find James.

    I don't think I would have figured out that supplying a property accessor on the cell's data value item would have a massive impact on sorting performance unless I spent a lot of time in a profiler . . .
  • 6. Re: TableView sorting my clicking column Header
    James_D Guru
    Currently Being Moderated
    I was actually guessing the reflective calls that have to be present in the PropertyValueFactory were the culprit, so I went to implement a cell value factory directly. When I had to return an ObservableValue<String> and realized the Person class wasn't giving me access to one, the light bulb went on...

    It turns out that the guess about reflection was not so wrong. The PropertyValueFactory does some caching of the value specifically because performance would be affected otherwise. Setting a cell factory saves a little additional time, but not much.

    The JavaFX8 API is still helpful; you can subclass TableView and override sort(), or (and this is probably better) plug in a sorting strategy by calling setSortPolicy(...). This gives the opportunity to remove a little of the indirection of getting the cell value factory from the column and then getting the cell value from that. (I guess TableColumn.setComparator() can be used to similar effect.) Again that's a very minor performance increment compared to providing the property accessors, but it's nice to have the option. Using that also gave me the chance to time the sorting: I was getting around 0.16 seconds to sort on a single column, which is plenty fast enough. It's interesting to note that the first sort took 3-4 times longer, but you already knew that...
  • 7. Re: TableView sorting my clicking column Header
    b.pradeep Newbie
    Currently Being Moderated

    Thanks james for wonderful answer,

     

     

    Now am in a situation.... Problem is I need to implement "active sort" with new row insertion every 15 - 20 milliseconds,
    What am currently doing is to reapplysortorder every time a data point is inserted

     

     

     

    private void reapplyTableSortOrder() {

            ArrayList<TableColumn<Person, ?>> sortOrder = new ArrayList<TableColumn<Person, ?>>(table.getSortOrder());;

            table.getSortOrder().clear();

            table.getSortOrder().addAll(sortOrder);

        }

     

    but this is not good in performance (I have looked at swing example which is way faster).

     

    Any help will be really appreciated....

  • 8. Re: TableView sorting my clicking column Header
    James_D Guru
    Currently Being Moderated

    I'd probably try to insert the new data in the correct location, rather than adding it (at the end?) and then sorting again after. It might take a bit of work to figure out what that location is, but the idea would be to build a Comparator from the sortOrder, and then to use Collections.binarySearch(List<T>, T, Comparator<T>) to get the correct location.

  • 9. Re: TableView sorting my clicking column Header
    b.pradeep Newbie
    Currently Being Moderated

    I have tried it but as am using observableList (as argument in tableview are required of this type)  I have to first convert it to array an dthen do a binary search.

     

    I have tried to implement my own binary searc (recursive version) but performance is bad as compared to first converting to array and then using Array.binarysearch.

     

    Any suggestion on how to do good binary search o large data of observable list

     


    Thanks

  • 10. Re: TableView sorting my clicking column Header
    James_D Guru
    Currently Being Moderated

    You can just use Collections.binarySearch(table.getItems(), ...).

  • 11. Re: TableView sorting my clicking column Header
    b.pradeep Newbie
    Currently Being Moderated

    James am quite new to java and javafx but I really need to implement these features and need your help...

     

    What I want is to have active sorting and auto insertion every 20ms, But as user can select whihc column he want to  sort on fly...

    am not able to properly parse on how to get which column's key is sorted and then how to properly pass my comparator.....

     

    and what if it is sorted by more than one column but this is a thing to pursue later right now I want something which can help me sort ascending and descending.

     

    I need comparator for int float and string

     

    Thanks

  • 12. Re: TableView sorting my clicking column Header
    James_D Guru
    Currently Being Moderated

    Something like

     

    import java.util.Collections;
    import java.util.Comparator;
    import javafx.application.Application;
    import javafx.beans.property.SimpleStringProperty;
    import javafx.beans.property.StringProperty;
    import javafx.collections.FXCollections;
    import javafx.collections.ObservableList;
    import javafx.event.ActionEvent;
    import javafx.event.EventHandler;
    import javafx.geometry.HPos;
    import javafx.scene.Scene;
    import javafx.scene.control.Button;
    import javafx.scene.control.Label;
    import javafx.scene.control.TableColumn;
    import javafx.scene.control.TableColumn.SortType;
    import javafx.scene.control.TableView;
    import javafx.scene.control.TextField;
    import javafx.scene.control.cell.PropertyValueFactory;
    import javafx.scene.layout.BorderPane;
    import javafx.scene.layout.ColumnConstraints;
    import javafx.scene.layout.GridPane;
    import javafx.stage.Stage;
    public class TableInsertExample extends Application {
      @Override
      public void start(Stage primaryStage) {
        final BorderPane root = new BorderPane();
        final TableView<Person> table = new TableView<Person>();
        table.setItems(createData());
        final TableColumn<Person, String> firstNameColumn = new TableColumn<>("First Name");
        final TableColumn<Person, String> lastNameColumn = new TableColumn<>("Last Name");
        firstNameColumn.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
        lastNameColumn.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));
        table.getColumns().addAll(firstNameColumn, lastNameColumn);
        
        final GridPane addPersonPane = new GridPane();
        final TextField firstNameTF = new TextField();
        final TextField lastNameTF = new TextField();
        final Button addButton = new Button("Add");
        addPersonPane.addRow(0, new Label("First Name:"), firstNameTF);
        addPersonPane.addRow(1, new Label("Last Name:"), lastNameTF);
        addPersonPane.addRow(2, addButton);
        final ColumnConstraints leftColConstraints = new ColumnConstraints();
        leftColConstraints.setHalignment(HPos.RIGHT);
        final ColumnConstraints rightColConstraints = new ColumnConstraints();
        rightColConstraints.setHalignment(HPos.LEFT);
        addPersonPane.getColumnConstraints().addAll(leftColConstraints, rightColConstraints);
        
        addButton.setOnAction(new EventHandler<ActionEvent>() {
          
          @Override
          public void handle(ActionEvent event) {
            final Person person = new Person(firstNameTF.getText(), lastNameTF.getText());
            int index ;
            final ObservableList<TableColumn<Person, ?>> tableSortOrder = table.getSortOrder();
            if (tableSortOrder.size()==0) {
              index = table.getItems().size();
            } else {
              Comparator<Person> comp = new Comparator<Person>() {
                @Override
                public int compare(Person person1, Person person2) {
                  for (TableColumn<Person, ?> col : tableSortOrder) {
                    Comparator colComp = col.getComparator(); 
                    if (colComp == null) {
                      colComp = TableColumn.DEFAULT_COMPARATOR;
                    }
                    final Object o1 = col.getCellData(person1);
                    final Object o2 = col.getCellData(person2);
                    int c = colComp.compare(o1, o2);
                    if (col.getSortType() == SortType.DESCENDING) {
                      c = -c ;
                    }
                    if (c != 0) {
                      return c;
                    }
                  }
                  return 0 ;
                }
              };
              index = Collections.binarySearch(table.getItems(), person, comp);
              if (index < 0) {
                index = -index-1 ;
              }
            }
            table.getItems().add(index, person);
          }
        });
        root.setCenter(table);
        root.setBottom(addPersonPane);
        primaryStage.setScene(new Scene(root, 400, 600));
        primaryStage.show();    
      }
      public static void main(String[] args) {
        launch(args);
      }
      
      private ObservableList<Person> createData() {
        return FXCollections.observableArrayList(
            new Person("Hugo" ,"Lloris"),
            new Person("Benoit", "Assou-Ekotto"),
            new Person("Jan", "Vertonghen"),
            new Person("Michael", "Dawson"),
            new Person("Kyle", "Walker"),
            new Person("Scott", "Parker"),
            new Person("Mousa", "Dembele"),
            new Person("Gylfi","Sigurdsson"),
            new Person("Gareth", "Bale"),
            new Person("Aaron", "Lennon"),
            new Person("Jermaine", "Defoe")
        );
      }
      
      public static class Person {
        private final StringProperty firstName ;
        private final StringProperty lastName ;
        Person(String firstName, String lastName) {
          this.firstName = new SimpleStringProperty(this, "firstName", firstName);
          this.lastName = new SimpleStringProperty(this, "lastName", lastName);
        }
        public String getFirstName() { return firstName.get(); }
        public void setFirstName(String firstName) { this.firstName.set(firstName);}
        public StringProperty firstNameProperty() { return firstName ; }
        public String getLastName() { return lastName.get(); }
        public void setLastName(String lastName) { this.lastName.set(lastName); }
        public StringProperty lastNameProperty() { return lastName ; }    
        @Override public String toString() { return firstName.get() + " " + lastName.get() ; }
        
      }
    }
    

     

    Message was edited by: James_D Added test for column sort type to handle descending sorts.

  • 13. Re: TableView sorting my clicking column Header
    b.pradeep Newbie
    Currently Being Moderated

    @James_D Can you please tell me why this last method in person class (toString is overridden, I mean what is use of it.)

     

    Thanks

    P.S. : I have mix of column five column <col1 : int, col2: float, col3 : int, col4 : float , col5 : String>

    Thanks james it solve half of my problem

  • 14. Re: TableView sorting my clicking column Header
    b.pradeep Newbie
    Currently Being Moderated

    Also I need to ask this thing

     

    now insertion performance is good but gui is not interactive
    I am clicking on rows but it is not selectiion (or taking too much time)

     

    also scrolling also become difficult

     

    gui is freezing in terms of scrolling....

     

    any suggestion for that

     

    P.S. am adding one person object every 20 millisecond.

     

    Thank

     

    Main culprit for slow insertion is

     

    table.getItems.add(index,person);

     

    any idea on how to improve....

     

    Message was edited by: b.pradeep Main culprit for slow insertion is table.getItems.add(index,person);

1 2 3 Previous Next

Legend

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