我正在研究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中的注释行)
答案 0 :(得分:4)
我无法复制你的问题。
将新行添加到TableView中的排序位置,其中包含100,000行,对我来说几乎是即时的。
我使用了詹姆斯对你上一个问题的回答的修改:JavaFx tableview sort is really slow how to improve sort speed as in java swing。
修改按下“添加”按钮后执行以下算法:
如果您使用Java 7,则TableView scrollTo例程中存在一个错误,该错误会阻止表在所有情况下滚动到正确的位置。
使用Java 8b93,Win7输出:
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());
}
}
}
我添加了我的问题用例
我不知道为什么你需要这种行为。
一些建议:
table.getItems.add(index,element)
,而不是创建一个全新的列表并调用table.getItems().setAll(newList)
。System.currentTimeMillis()
,否则当您报告平均0.2ms时,您的测量结果非常不准确,因为您只是平均0毫秒和1毫秒的值。由于我不相信这个问题广泛有用,所以我不会花更多的时间在上面。
如果它真的是一个问题,我请求你做(文件)它....而我正在做的是因为在基于摇摆的表(我不能发布它的代码)它更快(按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毫秒关键帧可能会错过一个脉冲并最终略微不均匀(尽管眼睛可能不会感觉到不均匀)。