简体   繁体   English

JavaFx tableview排序非常慢,如何提高排序速度,就像在java swing中一样

[英]JavaFx tableview sort is really slow how to improve sort speed as in java swing

I am working on javaFx application and have created a tableview and have around 100,000+ rows with 10 columns. 我正在研究javaFx应用程序,并创建了一个tableview,大约有100,000多行,包含10列。

I have also created same table using java swing Jtable. 我还使用java swing Jtable创建了相同的表。

Now I need sorting performance to be better in javaFx if not better than some what near to java swing's jtable. 现在我需要排序性能在javaFx中更好,如果不比java swing的jtable更接近。

Right Now I am using sortorder() which sort data by clicking on column headers and sort speed is 20 times slower than Jtable. 现在我使用sortorder()通过单击列标题对数据进行排序,排序速度比Jtable慢20倍。

Can anyone please help ? 有人可以帮忙吗?

Thank You 谢谢

Edit: 编辑:

I am using example 13.8 in this link http://docs.oracle.com/javafx/2/ui_controls/table-view.htm just has added few line of code to just add 100,000 rows by generating random data. 我在这个链接中使用示例13.8 http://docs.oracle.com/javafx/2/ui_controls/table-view.htm只添加了几行代码,通过生成随机数据来添加100,000行。

It took a while, but I think I have figured this out, at least for this example. 花了一段时间,但我想我已经弄明白了,至少在这个例子中。

In this example, the Person class doesn't have any property accessors (ie there's a getFirstName() method, but no firstNameProperty() method). 在此示例中,Person类没有任何属性访问器(即,有一个getFirstName()方法,但没有firstNameProperty()方法)。 Sorting by columns has to access the value in each cell in the column via the cell value factory. 按列排序必须通过单元格值工厂访问列中每个单元格中的值。 When there's no property accessor, the cell value factory is going to call getFirstName() and then wrap the result in a new ReadOnlyObjectWrapper on each invocation. 当没有属性访问器时,单元值工厂将调用getFirstName(),然后在每次调用时将结果包装在新的ReadOnlyObjectWrapper中。

If you make sure the class representing the row data has the appropriate property accessors, then retrieving the value is much more efficient, as it merely returns a reference to the existing StringProperty. 如果确保表示行数据的类具有适当的属性访问器,则检索该值会更有效,因为它只返回对现有StringProperty的引用。

This example sorts 100,000 rows in approximately a second on my system (MacBookPro 8GB RAM, quad core). 此示例在我的系统上大约一秒钟内排序100,000行(MacBookPro 8GB RAM,四核)。 You can improve performance more by providing an explicit cell value factory, which gets around the need for reflection in computing the cell value. 您可以通过提供显式单元值工厂来提高性能,这可以满足计算单元格值时反射的需要。 In other words, replace 换句话说,替换

    firstNameCol.setCellValueFactory(
            new PropertyValueFactory<Person, String>("firstName"));

with

    firstNameCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Person,String>, ObservableValue<String>>() {
      @Override
      public ObservableValue<String> call(CellDataFeatures<Person, String> cdf) {
        return cdf.getValue().firstNameProperty();
      }
    });

The performance saving here is not as dramatic. 这里的性能节省并不那么引人注目。

Here's the full example: 这是完整的例子:

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.Group;
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.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 void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage stage) {
        Scene scene = new Scene(new Group());
        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"));


        Random random = new Random();
        for (int i = 0; i < 100000; 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) {
                table.getItems().add(new Person(
                        addFirstName.getText(),
                        addLastName.getText(),
                        addEmail.getText()));
                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, 0, 0, 10));
        vbox.getChildren().addAll(label, table, hb);

        ((Group) scene.getRoot()).getChildren().addAll(vbox);

        stage.setScene(scene);
        stage.show();
    }

    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().compareTo(o.getFirstName());
    }
  }
} 

UPDATE: Note this is fixed in JavaFX 8. 更新:请注意,这已在JavaFX 8中修复。

I don't know what's up with the speed of the sort on the TableView when you click on the table header - it gets slow with 100,000 rows as you mention in your question. 当你点击表格标题时,我不知道TableView上的排序速度是多少 - 如你在问题中提到的那样,它变得很慢,有100,000行。

If you just provide a button which sorts the underlying collection, it sorts many times quicker and the table updates fine (at least under Java 8). 如果你只是提供一个对基础集合进行排序的按钮,它会更快地排序很多次并且表格更新(至少在Java 8下)。 Time to sort a column was well under a second. 对列进行排序的时间还不到一秒。

Button sortByEmail = new Button("Sort by Email");
sortByEmail.setOnAction(new EventHandler<ActionEvent>() {
  @Override public void handle(ActionEvent event) {
    Collections.sort(table.getItems(), new Comparator<Person>() 
      @Override public int compare(Person o1, Person o2) {
        return o1.getEmail().compareTo(o2.getEmail());
      }
    });
  }
});

Or using Java 8 lambdas: 或者使用Java 8 lambdas:

Button sortByEmail = new Button("Sort by Email");
sortByEmail.setOnAction(event -> 
    Collections.sort(
        table.getItems(), 
        (o1, o2) -> o1.getEmail().compareTo(o2.getEmail())
    )
);

So if you have a lot of items in your table, on the column call setSortable(false) and provide buttons for the user to sort table columns if you need to. 因此,如果表中有很多项,则在列上调用setSortable(false),并为用户提供按钮,以便在需要时对表列进行排序。

Plugging my laptop into the wall and increasing the maximum memory of the JVM also increased sort performance for large data sets (cutting the time to sort by clicking on a column header with the sample below from 20 seconds to about 10 seconds). 将我的笔记本电脑插入墙壁并增加JVM的最大内存也增加了大型数据集的排序性能(通过单击下面的样本从20秒到大约10秒单击列标题来缩短排序时间)。

File an enhancement request against the JavaFX issue tracker with the following sample code. 使用以下示例代码向JavaFX问题跟踪器提交增强请求。 The code generates 100,000 rows of random data, and you can check the performance of standard column sorting by clicking on a table column header to sort on a table column and compare that with the performance of sorting by Collections.sort invoked by pressing buttons. 该代码生成100,000行随机数据,您可以通过单击表列标题对表列进行排序来检查标准列排序的性能,并将其与按按钮调用的Collections.sort排序性能进行比较。

import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;

import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.Random;

public class Test extends Application {

    private TableView<Person> table = new TableView<Person>();
    private final ObservableList<Person> data =
            FXCollections.observableArrayList(
            new Person("Jacob", "Smith", "jacob.smith@example.com"),
            new Person("Isabella", "Johnson", "isabella.johnson@example.com"),
            new Person("Ethan", "Williams", "ethan.williams@example.com"),
            new Person("Emma", "Jones", "emma.jones@example.com"),
            new Person("Michael", "Brown", "michael.brown@example.com"));
    final HBox hb = new HBox();

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage stage) {
        Scene scene = new Scene(new Group());
        stage.setTitle("Table View Sample");
        stage.setWidth(550);
        stage.setHeight(550);

        final Label label = new Label("Address Book");
        label.setFont(new Font("Arial", 20));

        table.setEditable(true);

        TableColumn firstNameCol = new TableColumn("First Name");
        firstNameCol.setMinWidth(100);
        firstNameCol.setCellValueFactory(
                new PropertyValueFactory<Person, String>("firstName"));

        TableColumn lastNameCol = new TableColumn("Last Name");
        lastNameCol.setMinWidth(100);
        lastNameCol.setCellValueFactory(
                new PropertyValueFactory<Person, String>("lastName"));

        TableColumn emailCol = new TableColumn("Email");
        emailCol.setMinWidth(200);
        emailCol.setCellValueFactory(
                new PropertyValueFactory<Person, String>("email"));

        Random random = new Random();
        table.setItems(data);
        for (int i = 0; i < 100000; i++) {
          table.getItems().add(new Person(randomString(random), randomString(random), randomString(random)));
        }
        table.getColumns().addAll(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) {
                data.add(new Person(
                        addFirstName.getText(),
                        addLastName.getText(),
                        addEmail.getText()));
                addFirstName.clear();
                addLastName.clear();
                addEmail.clear();
            }
        });

        Button sortByFirstName = new Button("Sort by First Name");
        sortByFirstName.setOnAction(new EventHandler<ActionEvent>() {
          @Override
          public void handle(ActionEvent event) {
            Collections.sort(table.getItems());
          }
        });

        Button sortByEmail = new Button("Sort by Email");
        sortByEmail.setOnAction(new EventHandler<ActionEvent>() {
          @Override
          public void handle(ActionEvent event) {
            Collections.sort(table.getItems(), new Comparator<Person>() {
              @Override
              public int compare(Person o1, Person o2) {
                return o1.getEmail().compareTo(o2.getEmail());
              }
            });
          }
        });

        hb.getChildren().addAll(addFirstName, addLastName, addEmail, addButton, sortByFirstName, sortByEmail);
        hb.setSpacing(3);

        final VBox vbox = new VBox();
        vbox.setSpacing(5);
        vbox.setPadding(new Insets(10, 0, 0, 10));
        vbox.getChildren().addAll(label, table, hb);

        ((Group) scene.getRoot()).getChildren().addAll(vbox);

        stage.setScene(scene);
        stage.show();
    }

    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 SimpleStringProperty firstName;
        private final SimpleStringProperty lastName;
        private final SimpleStringProperty 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 String getLastName() {
            return lastName.get();
        }

        public void setLastName(String fName) {
            lastName.set(fName);
        }

        public String getEmail() {
            return email.get();
        }

        public void setEmail(String fName) {
            email.set(fName);
        }

    @Override
    public int compareTo(Person o) {
      return firstName.get().compareTo(o.getFirstName());
    }
  }
} 

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

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