我们在课程项目中建立了一个信息检索引擎。我们被要求使用JavaFX运行程序。
问题是该项目非常繁忙,其中包括:解析文档(460,000个文档和最多300万个单词),向词典中添加术语以及更多需要时间的功能。
没有GUI的项目运行所需的时间约为19分钟;它包括合并发布文件,并将字典从磁盘加载到RAM。问题在于,当我们添加GUI时,时间增加了近56分钟。
我们认为我们在构建的GUI中做错了:
Controller
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();
}*/
}
}
FXML文件
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.CheckBox?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Text?>
<AnchorPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="800.0" prefWidth="1200.0" xmlns="http://javafx.com/javafx/11.0.1">
<Button fx:id="btn_reset" layoutX="33.0" layoutY="70.0" mnemonicParsing="false" text="Reset" onAction='#resetIndexer' />
<Button fx:id="btn_browse_Corpus" layoutX="129.0" layoutY="112.0" mnemonicParsing="false" onAction="#setCorpusPath" text="Browse" />
<Button fx:id="btn_browse_postfile" layoutX="129.0" layoutY="149.0" mnemonicParsing="false" onAction="#setStopWord" text="Browse" />
<TableView fx:id="table_dic" layoutX="451.0" layoutY="70.0" prefHeight="439.0" prefWidth="427.0">
<columns>
<TableColumn fx:id="tc_term" prefWidth="211.20001220703125" text="Term " />
<TableColumn fx:id="tc_tf" minWidth="2.4000244140625" prefWidth="215.20001220703125" text="tf" />
</columns>
</TableView>
<TextField fx:id="tf_postfilePath" layoutX="204.0" layoutY="148.0" prefHeight="26.0" prefWidth="218.0" />
<TextField fx:id="tf_corpusPath" layoutX="205.0" layoutY="111.0" prefHeight="26.0" prefWidth="217.0" />
<CheckBox fx:id="checkBox_Stemming" layoutX="125.0" layoutY="74.0" mnemonicParsing="false" text="Stemming" />
<Button fx:id="btn_dictionary" layoutX="39.0" layoutY="233.0" mnemonicParsing="false" text="Dictionary" onAction="#showDictionary" />
<Button fx:id="btn_start" layoutX="35.0" layoutY="187.0" mnemonicParsing="false" text="Start" onAction='#startIndex' />
<Text layoutX="46.0" layoutY="129.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Corpus" />
<Text layoutX="33.0" layoutY="163.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Posting files" wrappingWidth="75.0" />
</AnchorPane>
主类
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);
}
}
感谢您的帮助,谢谢。
我遵循@Slaw和@LuxusProblem的答案;您应该创建一个服务,其唯一作用是执行指令indexer.Start(rf,corpusPath,checkBox_Stemming.isSelected(),postfilePath);
。然后在您的控制器中,您必须启动该服务,然后侦听该服务的状态;如果切换到成功,则可以显示您的消息框。
我已经为您的代码实现了一项基本服务(也许会犯一些小错误,因为我不知道Indexer
和ReadFile
类是如何制作的:]]]
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; } } }; } }
在您的控制器中,
startIndex
方法将如下所示:
@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(); } }
UPDATE
如果您想走得更远,甚至可以管理服务中抛出的异常。引发错误时,您的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; } }; }
在您的控制器中,将
State.FAILED
的大小写添加到服务状态侦听器:
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;
我希望它将对您有帮助:)
我在这里尝试使用Platform.runLater,但没有任何变化: