javafx tableview活动排序很慢

时间:2013-06-18 09:42:16

标签: java performance javafx-2 tableview javafx-8

我正在研究javafx tableview并创建了一个包含100,000行的表(三列一个int两个浮点数)。

我有积极的排序。要插入新行,首先使用二进制搜索搜索索引,然后使用table.getItems.add(index,element)插入索引;

但是每增加一个新行,每隔20毫秒,gui就会没有响应。

我添加了 table.setSelectionModel(null); 并且它固定了我的GUI,因此它似乎是慢速GUI背后的罪魁祸首。

但我也需要能够选择行.....

任何人都建议在这种情况下做什么......

PS :(在添加行 table.setSelectionModel(null); 之前,我尝试运行jprofiler并显示 javafx.scene中消耗了大量时间.control.TableCell $ 2.onChanged

修改

我的用例

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);  


    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;  
  }  
   }  
  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() ; }  

  }  
}  

这一行开始

//this line increase speed but then we can not even click on table as it will give someexception  
    table.setSelectionModel(null);

帮助我 增加平均插入速度 0.2 ms (代码中包含计算平均值的代码)

禁用任何选择( addPersonToTable 中的代码因此而被评论)

我希望能够选择一行但具有此代码的一些速度效率。 (我使用了Jprofiler,它显示主要时间用在 TableCell.onChanged

注意:此代码由James_D编写,我刚修改了一下(添加了行table.setSelectionModel(null);以及addPersonToTable中的注释行)

1 个答案:

答案 0 :(得分:4)

我无法复制你的问题。

将新行添加到TableView中的排序位置,其中包含100,000行,对我来说几乎是即时的。

我使用了詹姆斯对你上一个问题的回答的修改:JavaFx tableview sort is really slow how to improve sort speed as in java swing

修改按下“添加”按钮后执行以下算法:

  1. 如果未输入新的人员详细信息,只需生成一些新的随机人员详细信息。
  2. 是否对表项进行二进制搜索以查找插入索引。
  3. 在适当的索引处插入项目,选择新添加的行。
  4. 滚动表格以显示它。
  5. 如果您使用Java 7,则TableView scrollTo例程中存在一个错误,该错误会阻止表在所有情况下滚动到正确的位置。

    使用Java 8b93,Win7输出:

    sortsample

    TableSortPerformanceTest.java

    import javafx.application.Application;
    import javafx.beans.property.SimpleStringProperty;
    import javafx.beans.property.StringProperty;
    import javafx.beans.value.ObservableValue;
    import javafx.event.ActionEvent;
    import javafx.event.EventHandler;
    import javafx.geometry.Insets;
    import javafx.scene.Scene;
    import javafx.scene.control.*;
    import javafx.scene.control.TableColumn.CellDataFeatures;
    import javafx.scene.control.cell.PropertyValueFactory;
    import javafx.scene.layout.HBox;
    import javafx.scene.layout.StackPane;
    import javafx.scene.layout.VBox;
    import javafx.scene.text.Font;
    import javafx.stage.Stage;
    import javafx.util.Callback;
    
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.Date;
    import java.util.Random;
    
    public class TableSortPerformanceTest extends Application {
    
        public static final int INIT_LIST_SIZE = 100_000;
    
        @Override
        public void start(Stage stage) {
            Scene scene = new Scene(new StackPane());
            stage.setTitle("Table View Sample");
            stage.setWidth(550);
            stage.setHeight(550);
    
            final Label label = new Label("Address Book");
            label.setFont(new Font("Arial", 20));
    
            final TableView<Person> table = new TableView<Person>();
            table.setEditable(true);
    
            TableColumn<Person, String> firstNameCol = new TableColumn<Person, String>("First Name");
            firstNameCol.setMinWidth(100);
            firstNameCol.setCellValueFactory(
                    new PropertyValueFactory<Person, String>("firstName"));
            firstNameCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Person,String>, ObservableValue<String>>() {
              @Override
              public ObservableValue<String> call(CellDataFeatures<Person, String> cdf) {
                return cdf.getValue().firstNameProperty();
              }
            });
    
            TableColumn<Person, String> lastNameCol = new TableColumn<Person, String>("Last Name");
            lastNameCol.setMinWidth(100);
            lastNameCol.setCellValueFactory(
                    new PropertyValueFactory<Person, String>("lastName"));
    
    
    
            TableColumn<Person, String> emailCol = new TableColumn<Person, String>("Email");
            emailCol.setMinWidth(200);
            emailCol.setCellValueFactory(
                    new PropertyValueFactory<Person, String>("email"));
    
    
            final Random random = new Random();
            for (int i = 0; i < INIT_LIST_SIZE; i++) {
              table.getItems().add(new Person(randomString(random), randomString(random), randomString(random)));
            }
            table.getColumns().addAll(Arrays.asList(firstNameCol, lastNameCol, emailCol));
    
            long start = new Date().getTime();
            Collections.sort(table.getItems());
            long end   = new Date().getTime();
            System.out.println("Took: " + (end - start));
    
    
            final TextField addFirstName = new TextField();
            addFirstName.setPromptText("First Name");
            addFirstName.setMaxWidth(firstNameCol.getPrefWidth());
            final TextField addLastName = new TextField();
            addLastName.setMaxWidth(lastNameCol.getPrefWidth());
            addLastName.setPromptText("Last Name");
            final TextField addEmail = new TextField();
            addEmail.setMaxWidth(emailCol.getPrefWidth());
            addEmail.setPromptText("Email");
    
            final Button addButton = new Button("Add");
            addButton.setOnAction(new EventHandler<ActionEvent>() {
                @Override
                public void handle(ActionEvent e) {
                    String firstName = isEmpty(addFirstName.getText()) ? randomString(random) : addFirstName.getText();
                    String lastName  = isEmpty(addLastName.getText())  ? randomString(random) : addLastName.getText();
                    String email     = isEmpty(addEmail.getText())     ? randomString(random) : addEmail.getText();
                    Person person = new Person(firstName, lastName, email);
                    int idx = Collections.binarySearch(table.getItems(), person);
                    if (idx < 0) {
                        idx = -idx - 1;
                    }
                    table.getItems().add(idx, person);
                    table.getSelectionModel().select(idx);
                    table.scrollTo(idx);
    
                    addFirstName.clear();
                    addLastName.clear();
                    addEmail.clear();
                }
            });
    
            final HBox hb = new HBox(3);
            hb.getChildren().addAll(addFirstName, addLastName, addEmail, addButton);
    
            final VBox vbox = new VBox();
            vbox.setSpacing(5);
            vbox.setPadding(new Insets(10));
            vbox.getChildren().addAll(label, table, hb);
    
            ((StackPane) scene.getRoot()).getChildren().addAll(vbox);
    
            stage.setScene(scene);
            stage.show();
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    
        private boolean isEmpty(String string) {
            return (string == null || string.isEmpty());
        }
    
        private String randomString(Random random) {
          char[] chars = new char[20];
          for (int i = 0; i < 20; i++) {
            int nextInt = random.nextInt(26);
            nextInt += random.nextBoolean() ? 65 : 97;
            chars[i] = (char) nextInt;
          }
          return new String(chars);
        }
    
        public static class Person implements Comparable<Person> {
    
            private final StringProperty firstName;
            private final StringProperty lastName;
            private final StringProperty email;
    
            private Person(String fName, String lName, String email) {
                this.firstName = new SimpleStringProperty(fName);
                this.lastName = new SimpleStringProperty(lName);
                this.email = new SimpleStringProperty(email);
            }
    
            public String getFirstName() {
                return firstName.get();
            }
    
            public void setFirstName(String fName) {
                firstName.set(fName);
            }
    
            public StringProperty firstNameProperty() {
              return firstName ;
            }
    
            public String getLastName() {
                return lastName.get();
            }
    
            public void setLastName(String fName) {
                lastName.set(fName);
            }
    
            public StringProperty lastNameProperty() {
              return lastName ;
            }
    
            public String getEmail() {
                return email.get();
            }
    
            public void setEmail(String fName) {
                email.set(fName);
            }
    
            public StringProperty emailProperty() {
              return email ;
            }
    
        @Override
        public int compareTo(Person o) {
          return firstName.get().compareToIgnoreCase(o.getFirstName());
        }
      }
    } 
    
      

    我添加了我的问题用例

    我不知道为什么你需要这种行为。

    一些建议:

    1. 试试Java 8 early access。 Java 8的性能得到了很大改善,我没有注意到你使用选择模型的时间差异。 (但请注意,在针对您的示例运行时,Java 8的行突出显示在构建94中似乎已被破坏,因此您可能希望file an issue用于损坏的高亮渲染。
    2. 在您的问题中首先使用table.getItems.add(index,element),而不是创建一个全新的列表并调用table.getItems().setAll(newList)
    3. 使用System.nanoTime代替System.currentTimeMillis(),否则当您报告平均0.2ms时,您的测量结果非常不准确,因为您只是平均0毫秒和1毫秒的值。
    4. 由于我不相信这个问题广泛有用,所以我不会花更多的时间在上面。

        

      如果它真的是一个问题,我请求你做(文件)它....而我正在做的是因为在基于摇摆的表(我不能发布它的代码)它更快(按0.2毫秒的顺序)平均而言)

      我在Java 8早期访问版本中看到的备用行的闪烁是一个问题,我将尝试在一个更简单的程序和文件中复制JavaFX问题跟踪器。

      我不相信这里有任何关于表现的问题。是的,选择模型在Java 7中增加了一些(小)开销,但在Java 8中,开销几乎不可察觉。对于操作,我在JavaFX for Java8中测量与测量Swing相同的0.2ms。因此,在Java 8的选择模型处理的平台实现中已经进行了一些性能调整,我不认为需要进一步调整。

        

      你也可以建议我过滤一些东西

      最好在新问题而不是评论中提出新问题。

      但是,看看Java 8为此提供了什么。
      JavaFX for Java 8中添加了FilteredList。 另请查看Panemu's TiwulFX,其中包括表过滤功能(以及许多其他有用的功能),并查看它是否适合您的应用程序。

      一般方法建议

      不要将行如此快速地添加到表中,而是批量插入传入的行并将其添加到表中较少(例如每四分之一秒)。如果表每秒更新四次而不是每秒60次,则用户不会关心。

      次要观察

      如果要对场景进行非常频繁的更新,而不是每隔20毫秒触发一次KeyFrame和事件处理程序的时间轴,请使用AnimationTimer,只要系统收到脉冲就会触发用于处理(默认情况下,脉冲以规则的间隔每秒发生60次;例如每16.666毫秒)。这将最终处理得更顺利,因为时间线的20毫秒关键帧可能会错过一个脉冲并最终略微不均匀(尽管眼睛可能不会感觉到不均匀)。