我正在使用迭代器迭代90000个图书对象的链表。每本书都有书名、ISBN、作者和价格。
在我的GUI中,我有一个文本字段和一个按钮,可以在文本字段中显示这90000Books.toString((。用户可以搜索一本书、删除一本书或更新书中的字段,所以我觉得用户应该能够看到所有的书及其所有字段。
问题是,这花了太长时间,我得到了死亡沙滩球,它从来没有加载。当我将for循环更改为10或1000时,它运行良好。必须是O(n^2(,对吗?
或者还有其他问题吗?非常感谢。
@FXML
void refreshListButtonPressed(ActionEvent event) {
listBooksTextArea.clear();
bookbag.myIter.reset();
for(int i = 0; i < 1000; i++) {
listBooksTextArea.appendText(bookbag.myIter.getCurrent().getData().toString() +
"n");
bookbag.myIter.nextLink();
if(bookbag.myIter.atEnd()) {
listBooksTextArea.appendText(bookbag.myIter.getCurrent().getData().toString()+
"n");
}
}
}
理想情况下,我甚至不会使用for循环,我会使用while(!bookbag.myIter.atEnd(((,但for循环向我证明了代码是有效的,大小或效率是问题所在。
您的代码在以下几个方面效率低下:
TextArea
将文本存储为String
,这是一个不可变的对象。因此,每次调用appendText(...)
时,都会通过将现有字符加上新字符复制到一个新的字符串对象来创建一个新字符串。由于字符串的大小在每次迭代时基本上线性增长,因此这就变成了O(n^2)
操作TextArea
为整个文本创建一个UI节点。UI节点通常非常昂贵,在这里您要创建一个巨大的节点来布局、设置样式和显示所有90000行文本,其中绝大多数在任何给定时间都不会在屏幕上显示
您可以通过在StringBuilder
中连接字符串,然后设置TextArea
的文本一次,至少部分修复第一个问题,尽管这不会修复第二个问题。
这里的首选方法是使用虚拟化控件,例如ListView
或TableView
。这些控件创建有限数量的单元格(在ListView
的情况下,每行一个,在TableView
的情况下每行每列一个(,本质上只为可见数据创建单元格。当用户滚动时,单元格将被重用以显示新数据。这大大提高了性能。
此外,这些控件可能允许在UI和实际数据之间进行更好的交互。使用TableView
,每一行将表示一个Book
对象,并且该行中的每个单元格都是该对象的属性。如果使表可编辑,则可以验证对每个单元格的更改,即逐个属性验证更改。使用文本区域,您需要仔细分析对文本的更改,以确保结果是有效的图书列表。
我通常会建议大家学习TableView
的教程,例如本教程。简而言之,使用JavaFX Properties创建一个Book
类。创建一个TableView<Book>
,并使用setCellValueFactory(...)
方法将列配置为指向适当的属性。例如,可以通过在单元格工厂中为列提供TextFieldTableCell
来使数据可编辑。然后只需将您的Book
实例添加到表的items
列表中。