2013-06-18 3 views
1

Я работаю над табличным представлением javafx и создал таблицу со 100 000 строк (три столбца один int two float).javafx tableview active sort is slow

У меня есть активная сортировка. Чтобы вставить новую строку в первый индекс поиска, используя двоичный поиск, а затем вставив его, используя table.getItems.add (index, element);

Но с добавлением новой строки каждые 20 мс gui является небольшим бит не реагирующим.

Я добавил таблица.setSelectionModel (null);, и он крепит мой графический интерфейс, поэтому он кажется виновником медленного графического интерфейса.

Но я также должен возможность выбрать строки .....

кто посоветует, что делать в этом случае ....

P.S.: (Перед добавлением строки table.setSelectionModel (NULL); я попытался запустить JProfiler и показывали большое время потребляются в javafx.scene.control.TableCell $ 2.onChanged)

Edit:

Мой случай использования

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 мс в среднем (код для вычисления среднего включен в коде)

но отключить любой выбор (код в addPersonToTable комментируется из-за этого причина)

Я хочу, чтобы выбрать строку, но с некоторой эффективностью по скорости этого кода. (Я использовал JProfiler и это показывает основное время тратится на TableCell.onChanged)

Примечание: Этот код написан James_D я только модифицировал его немного (добавил линию table.setSelectionModel (нуль) и комментарий строки в addPersonToTable)

ответ

4

Я не могу воспроизвести вашу проблему.

Добавление новых строк в отсортированное положение в TableView со 100 000 строк в нем практически мгновенно для меня.

Я использовал модификацию ответа Джеймса на ваш предыдущий вопрос: JavaFx tableview sort is really slow how to improve sort speed as in java swing.

Модификация выполняет следующий алгоритм по нажатию на кнопку Добавить:

  1. Если нет новых подробностей человек не вошли, просто создает некоторые новые подробности случайного человека.
  2. Выполняет бинарный поиск на элементах таблицы, чтобы найти индекс вставки.
  3. Вставляет элемент в соответствующий индекс, выбирает вновь добавленную строку.
  4. Прокручивает таблицу, чтобы отобразить ее.

Если вы используете Java 7 есть ошибка в TableView scrollTo рутина, который предотвращает таблицу с прокруткой в ​​нужное место во всех ситуациях.

Вывод с использованием Java 8b93, Win7:

sortsample

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

Я добавил мой случай использования в вопросе

Я понятия не имею, зачем вам это поведение.

Некоторые советы:

  1. Попробуйте Java 8 early access. Производительность Java 8 значительно улучшена, и я не замечаю разницы в сроках, используете ли вы модель выбора или нет. (Однако обратите внимание, что рендеринг строк в строке 8 в строке 8 показан на рисунке 94 при запуске по вашему примеру, поэтому вам может понадобиться file an issue для разбитого выделения выделения).
  2. Используйте table.getItems.add(index,element), как вы упомянули сначала в своем вопросе, а не создаете совершенно новый список и вызываете table.getItems().setAll(newList).
  3. Используйте System.nanoTime вместо System.currentTimeMillis(), в противном случае ваши измерения довольно неточны, когда вы сообщаете в среднем 0,2 мс, потому что вы просто усредняете 0 миллисекунд и 1 миллисекунду.

Как я не считаю, этот вопрос в целом полезен, поэтому я не буду тратить на него больше времени.

Если это действительно проблема, я прошу вас сделать (файл) это .... И делаю это потому, что в таблице на основе swing (я не могу опубликовать свой код) она намного быстрее (в порядке 0,2 мс в среднем)

мерцании чередующихся строк, которые я вижу в Java 8 ранних релизов доступа является проблемой, которую я буду пытаться повторить в более простой программе и подать против отслеживания проблем JavaFX.

Я не верю, что здесь есть проблема, связанная с производительностью. Да, модель выбора добавляет некоторые (небольшие) накладные расходы в Java 7, но в Java 8 накладные расходы едва заметны. Для этой операции я измеряю те же 0.2 мс в JavaFX для Java8, как и для Swing. Итак, в реализации платформы обработки выборки для Java 8 уже реализована определенная настройка производительности, и я не считаю, что дальнейшая настройка требуется.

также вы можете предложить мне что-нибудь на фильтрации

Лучше задавать новые вопросы в новые вопросы, а не комментарии.

Однако взгляните на то, что Java 8 может предложить для этого.
В JavaFX для Java добавлен FilteredList. Также посмотрите на Panemu's TiwulFX, который включает возможности фильтрации таблиц (среди многих других полезных вещей) и посмотреть, подходит ли оно вашему приложению.

Общий подход Предложение

Не добавлять строки так быстро к столу, вместо партии при входящем строка вставляет и добавить их в таблицу реже (например, каждые четверть второго). Пользователь не заботится о том, обновляется ли таблица четыре раза в секунду вместо 60 раз в секунду.

Minor Наблюдение

Если вы хотите сделать очень частые обновления на сцену, вместо временной шкалы, которая имеет ключевой кадр и обработчик события, который стреляет каждые 20 миллисекунд, используйте AnimationTimer который будет срабатывать каждый раз, когда система получает импульс для обработки (по умолчанию импульсы происходят с регулярными интервалами 60 раз в секунду, например каждые 16,666 миллисекунды). Это будет в конечном итоге обрабатывать материал более плавно, потому что 20-миллисекундный ключевой кадр временной шкалы может пропустить пульс и в итоге слегка неравномерно (хотя глаз, вероятно, может не восприниматься неравномерностью).

+0

Спасибо за ответ Но это не сработает по моему требованию Я добавил свой пример использования + лучшее описание моей проблемы ..... – learner

+0

Добавил дополнительную информацию в ответ на вашу модификацию вопроса. – jewelsea

+0

ok спасибо за помощь Я использовал table.getItems.add (индекс, элемент), но делаю это, разделив список немного fatser. Я не expreinced programmr и оленья кожа точно, как написать вопрос, так что я не мог бы подать его ... Если это действительно проблема, которую я прошу вас сделать это .... И делаю потому что в таблице на основе swing (я не могу опубликовать свой код) она намного быстрее (в порядке 0.2 мс в среднем я буду использовать наносекунду, как вы предложили для дальнейшего измерения) – learner