User interface JavaFX应用程序非常慢
作为我们课程项目的一部分,我们构建了一个信息检索引擎。我们被要求用JavaFX运行我们的程序 问题是这个项目非常繁忙,包括:解析文档(460000个文档和多达300万个单词)、向字典添加术语以及更多需要时间的功能 项目在没有GUI的情况下运行所需的时间约为19分钟;它包括合并post文件和将字典从磁盘加载到RAM。 问题是,当我们添加GUI时,时间会成倍增加,几乎达到56分钟 我们认为我们在构建的GUI中做错了什么: 控制器User interface JavaFX应用程序非常慢,user-interface,javafx,information-retrieval,User Interface,Javafx,Information Retrieval,作为我们课程项目的一部分,我们构建了一个信息检索引擎。我们被要求用JavaFX运行我们的程序 问题是这个项目非常繁忙,包括:解析文档(460000个文档和多达300万个单词)、向字典添加术语以及更多需要时间的功能 项目在没有GUI的情况下运行所需的时间约为19分钟;它包括合并post文件和将字典从磁盘加载到RAM。 问题是,当我们添加GUI时,时间会成倍增加,几乎达到56分钟 我们认为我们在构建的GUI中做错了什么: 控制器 package sample; import javafx.even
package sample;
import javafx.event.ActionEvent;
import javafx.scene.control.Alert;
import javafx.scene.control.TableView;
import javafx.stage.DirectoryChooser;
import javafx.stage.Stage;
import main.Indexer;
import main.ReadFile;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
public class Controller {
public javafx.scene.control.CheckBox checkBox_Stemming;
public javafx.scene.control.Button btn_start;
public javafx.scene.control.Button btn_browse_Corpus;
public javafx.scene.control.Button btn_browse_postfile;
public javafx.scene.control.Button btn_reset;
public javafx.scene.control.TextField tf_postfilePath;
public javafx.scene.control.TextField tf_corpusPath;
public javafx.scene.control.Button btn_dictionary;
public TableView table_dic;
public javafx.scene.control.TableColumn tc_term;
public javafx.scene.control.TableColumn tc_tf;
private Stage mainStage;
private Indexer indexer;
private ReadFile rf;
public void initialize(Stage mainStage) {
this.mainStage = mainStage;
mainStage.setMinHeight(600);
mainStage.setMinWidth(800);
}
public String openFile(ActionEvent event) {
DirectoryChooser chooser = new DirectoryChooser();
File defaultDirectory = new File("C:\\");
chooser.setInitialDirectory(defaultDirectory);
File selectedDirectory = chooser.showDialog(new Stage());
return selectedDirectory.getPath();
}
public void setStopWord(ActionEvent event){
tf_postfilePath.textProperty().setValue(openFile(event));
}
public void setCorpusPath(ActionEvent event){
tf_corpusPath.textProperty().setValue(openFile(event));
}
public void startIndex(ActionEvent event) {
String corpusPath = tf_corpusPath.textProperty().getValue();
String postfilePath = tf_postfilePath.textProperty().getValue();
if(corpusPath.length() > 0 && postfilePath.length() > 0 ){
long startTime = System.currentTimeMillis();
indexer=new Indexer();
rf= new ReadFile();
try {
indexer.Start(rf,corpusPath,checkBox_Stemming.isSelected(),postfilePath);
} catch (IOException e) {
e.printStackTrace();
}
long stopTime = System.currentTimeMillis();
long elapsedTime = stopTime - startTime;
long minutes = TimeUnit.MILLISECONDS.toMinutes(elapsedTime);
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("Information");
alert.setHeaderText("Look, an Information Dialog");
alert.setContentText("Number of documents:"+ main.Indexer.n+"\n"
+"Number of uniq terms:"+ "\n"+"Running time:"+minutes+ "minutes\n");
alert.showAndWait();
} else if (postfilePath.length() > 0) {
indexer = new Indexer();
try {
indexer.createFinalDictionary(postfilePath);
} catch (IOException e) {
e.printStackTrace();
}
} else {
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setTitle("Problem");
alert.setHeaderText("Look, an Information Dialog");
alert.setContentText("The path are empty or both not. please insert only one path postingfile or corpus");
alert.showAndWait();
}
}
public void resetIndexer(ActionEvent event) {
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("Information");
alert.setHeaderText("Look, an Information Dialog");
alert.setContentText("The dictionary and the postingfile deleted");
alert.showAndWait();
indexer = null;
rf = null;
tf_postfilePath.textProperty().setValue("");
tf_corpusPath.textProperty().setValue("");
}
public void showDictionary(ActionEvent event) {
System.out.println("hello");
/**if(indexer != null) {
HashMap<String, String> dic = indexer.getDic();
List<String> sortedKeys = new ArrayList(dic.keySet());
Collections.sort(sortedKeys);
for (String k:sortedKeys) {
table_dic.getColumns().addAll(k);
} else {
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("Information");
alert.setHeaderText("Look, an Information Dialog");
alert.setContentText("The dictionary is empty");
alert.showAndWait();
}*/
}
}
非常感谢您的帮助,谢谢。我关注@Slaw和@luxUSP问题的答案;您应该创建一个服务,其唯一作用是执行指令
indexer.Start(rf、corpusPath、checkBox\u Stemming.isSelected()、postfilePath)代码>。然后在控制器中,您必须启动此服务,然后监听此服务的状态;如果切换到Successed,则可以显示消息框
我已经为您的代码实现了一个基本服务(可能会有一些错误,因为我不知道Indexer
和ReadFile
类是如何生成的):
更新
如果您想更进一步,您甚至可以管理抛出到服务中的异常。当抛出错误时,您的IndexService
将切换为FAILED,以便您可以显示它
在IndexService
类中,只需删除try catch
:
@Override
protected Task<Boolean> createTask() {
return new Task<Boolean>() {
@Override
protected Boolean call() throws Exception {
indexer.Start(readFile, corpusPath, stem, postfilePath);
return true;
}
};
}
我希望它能帮助您:)可能与性能问题无关,但您使用fxml是完全错误的(控制器和加载的页面之间没有关系)-请确保了解通过fxml注入ui的基本知识:)绝对不相关:请学习java命名约定并坚持这些约定。问题可能是,您在GUI线程中开始索引(一项流程繁重的任务),在方法startIndex
的eventscope中,您应该启动一个线程,该线程不会中断GUI线程。当这个任务完成时,通过Platform.runLater(()->{})
Asside检索信息,因为您正在JavaFX应用程序线程上运行一切,正如Luxusproblem所提到的:JavaFX当然会占用一些资源(线程、内存)。根据这些资源的紧张程度,这可能或多或少是一个问题……就我个人而言,我不会在Task#call()
方法中捕获IOException
,而是让异常传播。这样,服务
进入失败
状态,异常存储在异常
属性中。是的,好主意,今晚我将更新我的答案,以便它可以管理失败的服务状态。
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("IR2020");
primaryStage.setScene(new Scene(root, 300, 275));
Controller controller=new Controller();
controller.initialize(primaryStage);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
package sample;
import java.util.concurrent.TimeUnit;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
public class IndexService extends Service<Boolean> {
private String corpusPath;
private String postFilePath;
private ReadFile readFile;
private boolean stemming;
public IndexService(String cPath, String pfPath, ReadFile rf, boolean stem) {
corpusPath = cPath;
postFilePath = pfPath;
readFile = rf;
stemming = stem;
}
@Override
protected Task<Boolean> createTask() {
return new Task<Boolean>() {
@Override
protected Boolean call() throws Exception {
try {
indexer.Start(readFile, corpusPath, stem, postfilePath);
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
};
}
}
@FXML
public void startIndex(ActionEvent event) throws InterruptedException {
String corpusPath = tf_corpusPath.textProperty().getValue();
String postfilePath = tf_postfilePath.textProperty().getValue();
if (corpusPath.length() > 0 && postfilePath.length() > 0) {
long startTime = System.currentTimeMillis();
IndexService service = new IndexService(corpusPath, postfilePath, rf, checkBox_Stemming.isSelected());
service.start();
service.stateProperty().addListener((bean_p, old_p, new_p) -> {
switch (new_p) {
case SUCCEEDED:
long stopTime = System.currentTimeMillis();
long elapsedTime = stopTime - startTime;
long minutes = TimeUnit.MILLISECONDS.toMinutes(elapsedTime);
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("Information");
alert.setHeaderText("Look, an Information Dialog");
alert.setContentText("Number of documents:" + "main.Indexer.n" + "\n" + "Number of uniq terms:" + "\n" + "Running time:" + minutes + "minutes\n");
alert.showAndWait();
break;
default:
break;
}
});
} else if (postfilePath.length() > 0) {
indexer = new Indexer();
try {
indexer.createFinalDictionary(postfilePath);
} catch (IOException e) {
e.printStackTrace();
}
} else {
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setTitle("Problem");
alert.setHeaderText("Look, an Information Dialog");
alert.setContentText("The path are empty or both not. please insert only one path postingfile or corpus");
alert.showAndWait();
}
}
@Override
protected Task<Boolean> createTask() {
return new Task<Boolean>() {
@Override
protected Boolean call() throws Exception {
indexer.Start(readFile, corpusPath, stem, postfilePath);
return true;
}
};
}
case FAILED:
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setTitle("Error");
alert.setHeaderText("Look, an Information Dialog");
alert.setContentText(service.getException().getMessage());
alert.showAndWait();
break;