I am working on javafx tableview and have created a table with 100,000 rows (three columns one int two float).
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);
But with addition of new row every 20 ms gui is litttle bit non responsive.
I added table.setSelectionModel(null); and it fasten my GUI so it seems to be culprit behind slow GUI.
But I also need ability to select rows.....
an anyone suggest what to do in this case....
P.S. :(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 )
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)
but it disable any select (code in addPersonToTable is commented because of this reason )
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 )
Note: This code is written by James_D I just modified it a little (added line table.setSelectionModel(null); and comment lines in addPersonToTable)