简体   繁体   English

javafx tableview活动排序很慢

[英]javafx tableview active sort is slow

I am working on javafx tableview and have created a table with 100,000 rows (three columns one int two float). 我正在研究javafx tableview并创建了一个包含100,000行的表(三列一个int两个浮点数)。

I have active sorting on. 我有积极的排序。 To insert a new row am first searching index using binary search and then inserting it using table.getItems.add(index,element); 要插入新行,首先使用二进制搜索搜索索引,然后使用table.getItems.add(index,element)插入索引;

But with addition of new row every 20 ms gui is litttle bit non responsive. 但是每增加一个新行,每隔20毫秒gui就会有点无响应。

I added table.setSelectionModel(null); 我添加了table.setSelectionModel(null); and it fasten my GUI so it seems to be culprit behind slow GUI. 它固定了我的GUI所以它似乎是慢速GUI背后的罪魁祸首。

But I also need ability to select rows..... 但我还需要能够选择行.....

an anyone suggest what to do in this case.... 任何人都建议在这种情况下做什么....

PS :(Before adding line table.setSelectionModel(null); I tried to run jprofiler and it was showing major time is consumed in javafx.scene.control.TableCell$2.onChanged ) PS :(在添加行table.setSelectionModel(null)之前;我试图运行jprofiler,它显示在javafx.scene.control.TableCell $ 2.onChanged中消耗了大量时间)

Edit: 编辑:

My use case 我的用例

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 in start 这行开始了

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

helps me increasing speed of insertion to 0.2 ms on average (code for computing average is included in code) 有助于我增加插入的速度,以0.2毫秒的平均(用于计算平均代码被包括在代码)

but it disable any select (code in addPersonToTable is commented because of this reason ) 但它禁用任何选择(因为这个原因, addPersonToTable中的代码被注释)

I want ability to select a row but with some speed efficiency of this code. 我希望能够选择一行但具有此代码的一些速度效率。 (I used Jprofiler and it shows main time is spent in TableCell.onChanged ) (我使用了Jprofiler,它显示了在TableCell.onChanged中花费的主要时间)

Note: This code is written by James_D I just modified it a little (added line table.setSelectionModel(null); and comment lines in addPersonToTable) 注意:这段代码是由James_D编写的,我刚修改了一下(添加了行table.setSelectionModel(null);并在addPersonToTable中添加了注释行)

I can't replicate your issue. 我无法复制你的问题。

Adding new rows to a sorted position in a TableView with 100,000 rows in it is virtually instantaneous to me. 将新行添加到TableView中的排序位置,其中包含100,000行,这对我来说几乎是即时的。

I used a modification of James answer to your previous question: JavaFx tableview sort is really slow how to improve sort speed as in java swing . 我使用了James对前一个问题的修改: JavaFx tableview排序如何提高排序速度,就像在java swing中一样

The modification performs the following algorithm on pressing the Add button: 按下“添加”按钮时,修改会执行以下算法:

  1. If no new person details are entered, just generates some new random person details. 如果未输入新的人员详细信息,则只生成一些新的随机人员详细信息。
  2. Does a binary search on the table items to find the insertion index. 是否对表项进行二进制搜索以查找插入索引。
  3. Inserts the item at the appropriate index, selects the newly added row. 在适当的索引处插入项目,选择新添加的行。
  4. Scrolls the table to display it. 滚动表格以显示它。

If you use Java 7 there is a bug in the TableView scrollTo routine which prevents the table from scrolling to the right location in all situations. 如果您使用Java 7,则TableView scrollTo例程中存在一个错误,该错误会阻止表在所有情况下滚动到正确的位置。

Output using Java 8b93, Win7: 使用Java 8b93,Win7输出:

sortsample

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

I have added my use case in question 我已经添加了我的用例

I have no idea why you require this behavior. 我不知道为什么你需要这种行为。

Some advice: 一些忠告:

  1. Try the Java 8 early access . 尝试Java 8早期访问 Java 8's performance is much improved and I don't notice a difference in the timing whether you use a selection model or not. Java 8的性能得到了很大改善,我没有注意到你使用选择模型的时间差异。 (Note however, Java 8's rendering of row highlights seems broken in build 94 when run against your example, so you may want to file an issue for the broken highlight rendering). (但是请注意,行亮点的Java 8的渲染时,对你的例子运行在建94似乎打破,所以你可能要提交的问题进行中碎亮点渲染)。
  2. Use table.getItems.add(index,element) as you mention first in your question rather than creating a completely new list and invoking table.getItems().setAll(newList) . 正如您在问题中首先提到的那样使用table.getItems.add(index,element) ,而不是创建一个全新的列表并调用table.getItems().setAll(newList)
  3. Use System.nanoTime instead of System.currentTimeMillis() , otherwise your measurements are pretty inaccurate when you report an average of 0.2ms because you are just averaging 0 millisecond and 1 millisecond values. 使用System.nanoTime而不是System.currentTimeMillis() ,否则当您报告平均0.2ms时,您的测量结果非常不准确,因为您只是平均0毫秒和1毫秒的值。

As I do not believe this question is broadly useful, so I will not spend further time on it. 因为我不相信这个问题广泛有用,所以我不会花更多的时间在它上面。

If it is really a issue I request you to do (file) it.... And am doing is because in swing based table (I can't post its code) it is much faster (in order of 0.2 ms in average) 如果它真的是一个问题,我请求你做(文件)它....我做的是因为在基于摇摆的表(我不能发布它的代码)它更快(平均0.2毫秒的顺序)

The flickering of alternate rows which I see in Java 8 early access releases is a problem, which I will try to replicate in a simpler program and file against the JavaFX issue tracker. 我在Java 8早期访问版本中看到的备用行的闪烁是一个问题,我将尝试在更简单的程序和文件中复制JavaFX问题跟踪器。

I don't believe there is any issue to be filed here with regards to performance. 我不认为这里有任何关于表现的问题。 Yes, the selection model adds some (small) overhead in Java 7, but in Java 8, the overhead is barely perceptible. 是的,选择模型在Java 7中增加了一些(小)开销,但在Java 8中,开销几乎不可察觉。 For the operation, I measure the same 0.2ms in JavaFX for Java8 as you measured for Swing. 对于操作,我在JavaFX for Java8中测量与测量Swing相同的0.2ms。 So, there has already been some performance tweaking in the platform implementation of the selection model processing for Java 8 and I don't believe any further tweaking is required. 因此,在Java 8的选择模型处理的平台实现中已经进行了一些性能调整,我不认为需要进一步调整。

also can you suggest me something on filtering 你也可以建议我过滤一些东西

Best to ask new questions in new questions rather than comments. 最好在新问题而不是评论中提出新问题。

However, take a look at what Java 8 has to offer for this. 但是,看看Java 8为此提供了什么。
There was a FilteredList added to JavaFX for Java 8. Also look at Panemu's TiwulFX which includes table filtering capabilities (among many other useful things) and see if it suits your application. JavaFX for Java 8中添加了FilteredList 。另请参阅Panemu的TiwulFX ,其中包括表过滤功能(以及许多其他有用的功能),并查看它是否适合您的应用程序。

General Approach Suggestion 一般方法建议

Don't add rows so rapidly to the table, instead batch your incoming row inserts up and add them to the table less often (eg every quarter second). 不要将行如此快速地添加到表中,而是批量插入传入的行并将其更少地添加到表中(例如每四分之一秒)。 The user isn't going to care if the table is updated four times a second instead of 60 times a second. 如果表每秒更新四次而不是每秒60次,则用户不会关心。

Minor Observation 次要观察

If you want to make very frequent updates to a scene, instead of a timeline which has a KeyFrame and an event handler that fires every 20 milliseconds, use an AnimationTimer that will be fired whenever the system receives a pulse for processing (by default pulses occur on regular intervals 60 times a second; eg every 16.666 milliseconds). 如果要对场景进行非常频繁的更新,而不是具有KeyFrame和每20毫秒触发一次的事件处理程序的时间轴,请使用每当系统接收到脉冲进行处理时将触发的AnimationTimer (默认情况下会发生脉冲)定期间隔60次;例如每16.666毫秒)。 This is going to end up processing stuff more smoothly because the 20 millisecond keyframe of the timeline could miss a pulse and end up slightly uneven (though the eye likely might not perceive to unevenness). 这将最终处理得更顺利,因为时间线的20毫秒关键帧可能会错过一个脉冲并最终略微不均匀(尽管眼睛可能不会感觉到不均匀)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM