JavaFx tableview sort is really slow, how to improve sorting speed like in java swing

I am working on a javaFx application and created a table view and contains about 100,000 rows with 10 columns.

I also created the same table using java swing Jtable.

Now I need sorting performance to be better in javaFx, if not better than java swing jtable.

Right now I use sortorder (), which sort the data by clicking on the column headers and the sorting speed is 20 times slower than Jtable.

Can anybody help?

thanks

Edit:

I use example 13.8 in this link http://docs.oracle.com/javafx/2/ui_controls/table-view.htm just added a few lines of code to just add 100,000 lines, generating random data.

+4
source share
2 answers

It took some time, but I think I understood this, at least for this example.

In this example, the Person class does not have any property accessories (i.e. there is a getFirstName () method, but not a firstNameProperty () method). Column sorting has access to the value in each cell in the column through the value of the factory cell. When there is no property accessor, the value of the factory cell will call getFirstName (), and then complete the result in a new ReadOnlyObjectWrapper with each call.

If you make sure that the class that represents the string data has the appropriate property attributes, then getting the value is much more efficient since it simply returns a reference to an existing StringProperty.

This example sorts 100,000 rows in about a second on my system (MacBookPro operating system 8 GB, quad-core processor). You can improve performance by providing an explicit factory cell value that bypasses the need for reflection when calculating 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(); } }); 

Maintaining performance here is not so dramatic.

Here is a complete 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 that this is fixed in JavaFX 8.

+7
source

I don’t know what happens with the sorting speed in TableView when you click on the table title - it slows down from 100,000 rows, as you mention in your question.

If you just provide a button that sorts the base collection, it sorts many times faster and the table updates perfectly (at least in Java 8). The column sorting time was significantly lower than 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:

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

So, if there are a lot of elements in your table, setSortable (false) is called in the column and provide buttons for sorting the user by the table columns, if you need to.

Connecting the laptop to the wall and increasing the maximum JVM memory also increased the sorting performance for large data sets (reducing the sorting time by clicking the column heading with a sample below from 20 seconds to 10 seconds).

Submit an extension request to the JavaFX error tracker with the following code sample. The code generates 100,000 rows of random data, and you can check the performance of standard column sorting by clicking the column heading of the table to sort the column of the table and compare this to the sorting performance of Collections.sort called up by clicking the buttons.

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

Source: https://habr.com/ru/post/1483279/


All Articles