1 2 3 Previous Next 32 Replies Latest reply: Jun 18, 2013 9:19 PM by b.pradeep Go to original post RSS
      • 15. Re: TableView sorting my clicking column Header
        b.pradeep

        James_D I need to disable TableCell.onChanged event as it is consuming all my time,,,, is there any way and is it safe ??

        • 16. Re: TableView sorting my clicking column Header
          James_D

          b.pradeep wrote:

           

          James_D I need to disable TableCell.onChanged event as it is consuming all my time,,,, is there any way and is it safe ??

          No: if you could, your cells wouldn't update when they needed to display new values (when you scrolled, for example, or when new items were added above the items in view).

           

          Main culprit for slow insertion is

           

          table.getItems.add(index,person);

           

          any idea on how to improve....

          But of course it is. You're adding about 50 new items every second. The ObservableList is implemented with an ArrayList, so each time you add an item, any items below it need to be shifted down by one position. If you're starting with 100,000 items, then on average each addition requires 50,000 items to be moved in an array.

           

          I think you need to question whether what you're trying to do is really feasible here. You want a table with 100,000+ entries, and you're adding new entries on the fly at the rate of 50-60 new items per second, while keeping the table sorted. That's a fairly steep requirement even without a GUI to display the results. But my real question is why you would want to do this. Even if you got it to work fast enough to be completely responsive, I can't see any way for a user to interact with this table. There is just too much data for a user to reasonably be able to look at, or navigate. And the rate of addition of new items means that things are going to be changing so fast that the user has no chance of seeing what's in the table anyway. It seems to me you might want to rethink your application requirements.

           

          Here's my mockup of this. It performs OK on my system if you stay near the top of the table; selection is pretty responsive most of the time, and scrolling works ok. If you scroll further down it gets a little less responsive but is more or less usable (maybe) most of the time. There are occasional pauses while (I think) the list tries to catch up with the insertions. I tried using a LinkedList instead of an ArrayList as the underlying list implementation but it didn't make any perceivable difference (I suspect the insertion is faster but the binary search is probably horrible).

           

           

          import java.util.ArrayList;
          import java.util.Collections;
          import java.util.Comparator;
          import java.util.List;
          import java.util.Random;
          
          import javafx.animation.Animation;
          import javafx.animation.KeyFrame;
          import javafx.animation.Timeline;
          import javafx.application.Application;
          import javafx.beans.binding.Bindings;
          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;
          import javafx.util.Duration;
          
          public class TableInsertExample extends Application {
          
            private static final Random RNG = new Random();
            private Comparator<Person> tableOrderComparator ;
          
            @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);
             
              tableOrderComparator = createTableOrderComparator(table);
             
              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());
                  addPersonToTable(table, person);
                }
              });
             
              Label countLabel = new Label();
              countLabel.textProperty().bind(Bindings.format("Table has %s entries", Bindings.size(table.getItems())));
          
              root.setTop(countLabel);
              root.setCenter(table);
              root.setBottom(addPersonPane);
              primaryStage.setScene(new Scene(root, 400, 600));
              primaryStage.show();   
             
              Timeline addRandomPeopleFrequently = new Timeline(new KeyFrame(Duration.millis(20), new EventHandler<ActionEvent>() {
                @Override
                public void handle(ActionEvent event) {
                  Person randomPerson = new Person(randomString(), randomString());
                  addPersonToTable(table, randomPerson);
                }
              }));
              addRandomPeopleFrequently.setCycleCount(Animation.INDEFINITE);
              addRandomPeopleFrequently.play();
            }
          
            private Comparator<Person> createTableOrderComparator(
                final TableView<Person> table) {
              return new Comparator<Person>() {
                @Override
                public int compare(Person person1, Person person2) {
                  for (TableColumn<Person, ?> col : table.getSortOrder()) {
                    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 ;
                }
              };
            }
          
            public static void main(String[] args) {
              launch(args);
            }
          
            private ObservableList<Person> createData() {
              List<Person> list = new ArrayList<>();
              for (int i=0; i<100_000; i++) {
                list.add(new Person(randomString(), randomString()));
              }
              return FXCollections.observableList(list);
            }
          
            private String randomString() {
              StringBuilder sb = new StringBuilder();
              for (int i=0; i<8; i++) {
                sb.append((char)(RNG.nextInt(26)+'a'));
              }
              return sb.toString();
            }
          
            private void addPersonToTable(final TableView<Person> table,
                final Person person) {
              int index ;
              final ObservableList<TableColumn<Person, ?>> tableSortOrder = table.getSortOrder();
              if (tableSortOrder.size()==0) {
                index = table.getItems().size();
              } else {
                index = Collections.binarySearch(table.getItems(), person, tableOrderComparator);
                if (index < 0) {
                  index = -index-1 ;
                }
              }
              table.getItems().add(index, person);
            }
          
            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() ; }
             
            }
          }
          
          • 17. Re: TableView sorting my clicking column Header
            b.pradeep

            Thank You for the answer James

             

            main reason i need to have this kind of table as we already have such a table in swing and i need almost equal performance, It is to check whther javafx can actually rreplace swing based table (mainly because javafx offer lot of feature and nice gui but we need performance as well. my work is mainly to check performance and also doesnot have any idea why it is needed.)

            • 18. Re: TableView sorting my clicking column Header
              b.pradeep

              James can you tell me exactly which function in TableCell cause this slow behaviour

               

              Other thing is can updating say 1000 rows per minute can be achieved without freezing my gui ???

              • 19. Re: TableView sorting my clicking column Header
                James_D

                b.pradeep wrote:

                 

                James can you tell me exactly which function in TableCell cause this slow behaviour

                 

                Not without spending a lot of time with a profiler. But my code runs much faster when the table is not sorted, so I'd assume it is table.getItems().add(index, item), or a consequence of that (such as updating subsequent items).

                Other thing is can updating say 1000 rows per minute can be achieved without freezing my gui ???

                As above, seems to work fine without the sort in place. You may need to find another approach to keeping the table sorted when adding new items. How did you manage this with the JTable?

                • 20. Re: TableView sorting my clicking column Header
                  b.pradeep

                  with jtable we have used array and used Sytem.arraycopy after using binary search to find index....

                  • 21. Re: TableView sorting my clicking column Header
                    b.pradeep

                    james I tried update it works efficiently with active sorting (I deleted a row at given index and then use binary search to add updated data ) 

                     

                    but when I tried delete its slow

                     

                    any possible reason ??

                    • 22. Re: TableView sorting my clicking column Header
                      James_D

                      So maybe try something like

                       

                        private void addPersonToTable(final TableView<Person> table,
                            final Person person) {
                          int index ;
                          final ObservableList<TableColumn<Person, ?>> tableSortOrder = table.getSortOrder();
                          if (tableSortOrder.size()==0) {
                            index = table.getItems().size();
                          } else {
                            index = Collections.binarySearch(table.getItems(), person, tableOrderComparator);
                            if (index < 0) {
                              index = -index-1 ;
                            }
                          }
                          List<Person> leftList = table.getItems().subList(0, index);
                          List<Person> rightList = table.getItems().subList(index, table.getItems().size());
                          List<Person> newList = new ArrayList<>(table.getItems().size()+1);
                          newList.addAll(leftList);
                          newList.add(person);
                          newList.addAll(rightList);
                          int selectedIndex = table.getSelectionModel().getSelectedIndex();
                          if (index < selectedIndex) {
                            selectedIndex++;
                          }
                          table.getItems().setAll(newList);
                          table.getSelectionModel().select(selectedIndex);
                        }
                      

                       

                      in my previous example.

                      • 23. Re: TableView sorting my clicking column Header
                        b.pradeep

                        and james can attaching to profiler effect performance

                         

                        i was executing code for update when I didn't attach jprofiler I am getting average of 0.007-0.01 ms (average over 1000 updates) but when I attach jprofiler I was getting something aorund 0.9ms

                        • 24. Re: TableView sorting my clicking column Header
                          b.pradeep

                          for line 15 am getting errorCannot instantiate the type ArrayList<?> , Syntax error on token "<", ? expected after this token , Type mismatch: cannot convert from ArrayList<?> to List<TableViewInt.Person> (I have tried ArrayList<Person> and it worked but still not much improvement in insertion )

                          • 25. Re: TableView sorting my clicking column Header
                            James_D

                            Well, exactly the same reason as add(index, person) was slow: the array list has to move a bunch of elements around.

                             

                            Why not implement "delete" in the same way as I just implemented "insert"?

                            • 26. Re: TableView sorting my clicking column Header
                              James_D

                              It should be

                              List<Person> newList = new ArrayList<>(table.getItems().size()+1);

                              (with nothing in the second set of angle brackets). This will compile (assuming you are using Java 7 or later).

                               

                              When I made this change to the sample code I supplied, it ran much faster. I was able to increase the frequency of inserting new data to once every 5ms, and ran it until the number of table rows doubled from 100,000 to 200,000. Scrolling and selection was responsive throughout.

                              • 27. Re: TableView sorting my clicking column Header
                                b.pradeep

                                ok will try

                                 

                                But what about error message I got from eclipse for using  List<Person> newList = new ArrayList<>(table.getItems().size()+1);
                                I am using java 7 but getting error..... (7.21)

                                • 28. Re: TableView sorting my clicking column Header
                                  James_D

                                  You might not have the compiler compliance level set to 1.7, under project properties -> Java Compiler.

                                  List<Person> newList = new ArrayList<Person>(...);

                                  will do exactly the same thing.

                                  • 29. Re: TableView sorting my clicking column Header
                                    b.pradeep

                                    @James_D

                                    Try this

                                    Now it is round 0.2 ms but problem is now I can't click as it will give some exception (I want to be able to delect row any suggestion)

                                    import java.util.ArrayList;
                                    import java.util.Collections;
                                    import java.util.Comparator;
                                    import java.util.List;
                                    import java.util.Random;
                                    import javafx.animation.Animation;
                                    import javafx.animation.KeyFrame;
                                    import javafx.animation.Timeline;
                                    import javafx.application.Application;
                                    import javafx.beans.binding.Bindings;
                                    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.SelectionMode;
                                    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;
                                    import javafx.util.Duration;
                                    public class TableInsertExample extends Application {
                                      int count=0;
                                        long s,e,mx=0,mn=1000000000;
                                        float avg=0;
                                      private static final Random RNG = new Random();
                                      private Comparator<Person> tableOrderComparator ;
                                      @SuppressWarnings("unchecked")
                                    @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<Person,String>("First Name");
                                        final TableColumn<Person, String> lastNameColumn = new TableColumn<Person,String>("Last Name");
                                        firstNameColumn.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
                                        lastNameColumn.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));
                                        table.getColumns().addAll(firstNameColumn, lastNameColumn);
                                       
                                        tableOrderComparator = createTableOrderComparator(table);
                                       
                                       //this line increase speed but then we can not even click on table as it will give someexception
                                        table.setSelectionModel(null);
                                      
                                        //table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
                                        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());
                                            addPersonToTable(table, person);
                                          }
                                        });
                                         table.getSortOrder().addAll(firstNameColumn);
                                        Label countLabel = new Label();
                                        countLabel.textProperty().bind(Bindings.format("Table has %s entries", Bindings.size(table.getItems())));
                                        root.setTop(countLabel);
                                        root.setCenter(table);
                                        root.setBottom(addPersonPane);
                                        primaryStage.setScene(new Scene(root, 400, 600));
                                        primaryStage.show();   
                                       
                                        Timeline addRandomPeopleFrequently = new Timeline(new KeyFrame(Duration.millis(20), new EventHandler<ActionEvent>() {
                                          @Override
                                          public void handle(ActionEvent event) {
                                            Person randomPerson = new Person(randomString(), randomString());
                                            count++;
                                            addPersonToTable(table, randomPerson);
                                          }
                                        }));
                                        addRandomPeopleFrequently.setCycleCount(Animation.INDEFINITE);
                                        addRandomPeopleFrequently.play();
                                      }
                                    
                                    
                                      private Comparator<Person> createTableOrderComparator(
                                          final TableView<Person> table) {
                                        return new Comparator<Person>() {
                                          @Override
                                          public int compare(Person person1, Person person2) {
                                            for (TableColumn<Person, ?> col : table.getSortOrder()) {
                                              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 ;
                                          }
                                        };
                                      }
                                    
                                      public static void main(String[] args) {
                                        launch(args);
                                      }
                                    
                                      private ObservableList<Person> createData() {
                                        List<Person> list = new ArrayList<Person>();
                                        for (int i=0; i<100000; i++) {
                                          list.add(new Person(randomString(), randomString()));
                                        }
                                        return FXCollections.observableList(list);
                                      }
                                    
                                      private String randomString() {
                                        StringBuilder sb = new StringBuilder();
                                        for (int i=0; i<8; i++) {
                                          sb.append((char)(RNG.nextInt(26)+'a'));
                                        }
                                        return sb.toString();
                                      }
                                    
                                      private void addPersonToTable(final TableView<Person> table,
                                           final Person person) {
                                         int index ;
                                         final ObservableList<TableColumn<Person, ?>> tableSortOrder = table.getSortOrder();
                                         if (tableSortOrder.size()==0) {
                                           index = table.getItems().size();
                                         } else {
                                           index = Collections.binarySearch(table.getItems(), person, tableOrderComparator);
                                           if (index < 0) {
                                             index = -index-1 ;
                                           }
                                         }
                                         s=System.currentTimeMillis();
                                         List<Person> leftList = table.getItems().subList(0, index);
                                         List<Person> rightList = table.getItems().subList(index, table.getItems().size());
                                         List<Person> newList = new ArrayList<Person>(table.getItems().size()+1);
                                         newList.addAll(leftList);
                                         newList.add(person);
                                         newList.addAll(rightList);
                                       /*  int selectedIndex = table.getSelectionModel().getSelectedIndex();
                                         if (index < selectedIndex) {
                                           selectedIndex++;
                                         }  */
                                         table.getItems().setAll(newList);
                                        // table.getSelectionModel().select(selectedIndex);
                                         e= System.currentTimeMillis() - s;
                                      avg+=e;
                                      if(mx<e)
                                      mx=e;
                                      if(mn>e)
                                      mn=e;
                                      if(count==1000)
                                      {
                                      avg=avg/10000;
                                      System.out.format("current System time is %f. Max is %d . Min is %d%n",avg,mx,mn);
                                      count=0;
                                      avg=0;
                                      mx=0;
                                      mn=100000000;
                                      }
                                       }
                                    
                                      /*private void addPersonToTable(final TableView<Person> table,
                                          final Person person) {
                                        int index ;
                                        final ObservableList<TableColumn<Person, ?>> tableSortOrder = table.getSortOrder();
                                        if (tableSortOrder.size()==0) {
                                          index = table.getItems().size();
                                        } else {
                                          index = Collections.binarySearch(table.getItems(), person, tableOrderComparator);
                                          if (index < 0) {
                                            index = -index-1 ;
                                          }
                                        }
                                        s=System.currentTimeMillis();
                                    
                                        table.getItems().add(index, person);
                                    
                                      e= System.currentTimeMillis() - s;
                                      avg+=e;
                                      if(mx<e)
                                      mx=e;
                                      if(mn>e)
                                      mn=e;
                                      if(count==1000)
                                      {
                                      avg=avg/10000;
                                      System.out.format("current System time is %f. Max is %d . Min is %d%n",avg,mx,mn);
                                      count=0;
                                      avg=0;
                                      mx=0;
                                      mn=100000000;
                                      }
                                      }
                                      */
                                    
                                    
                                      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: b.pradeep