Java Observable 列表在首次使用后导致速度极慢

Java Observable list causing immense slow down after first use

简介

您好,

我正在开发一个 java 应用程序,它使用多个双向哈希映射和可观察列表。我的代码有效,但我遇到了一个我似乎无法摆脱的奇怪怪癖。我在网上找不到任何关于此的信息,所以我想我会分享它,看看是否有其他人以前见过这个。

当代码第一次打开时,有一种方法可以查询 MySQL 数据库并提取作者列表的名称(该程序是一个图书馆数据库应用程序),其名称被分解分成不同的部分;名字、中间名和姓氏。然后,此方法遍历缓存的行集并将它们组合在一起,然后将它们添加到我之前提到的双向哈希映射和可观察列表中。然后在整个代码中出于各种目的使用它们。

只要在初始创建后刷新列表,就会出现此问题。 在程序 运行ning 期间,会不时对数据库进行新查询并刷新列表,以确保提供最新信息。该方法第一次是 运行,它执行我提到的所有这些步骤,大约需要 8 毫秒才能完成。但是当进行刷新时,该时间会跳到大约 1,400 毫秒。值得注意的是,刷新次数越多,时间越长。 显然,这是一个主要的时间峰值,但我看不到代码中的任何内容表明是什么原因造成的。我将在下面解释我是如何确定它是可观察列表的...

我在下面链接了一张图片以显示我正在谈论的示例。 请注意,正在处理的作者数量是相同的。其他打印输出是我同时测试的附加信息,与此 post 无关。

这是该方法的代码。

public void createAuthorHashMap() 抛出异常{ 字符串 authorFirstName, authorMiddleName, authorLastName, authorFullName; 整数 ID;

    // Get the Cached Row Set for all Authors in the database
    CachedRowSet authorList = connectionCommands.readDatabase(sqlCommands.selectAllAuthor);
        
    // Reset the contents of the hashmap and observable list
    bookAttributes.bidiMapAuthors.clear();
    bookAttributes.listAuthors.clear();
    choiceBoxAuthor.setItems(null);
        
    // Combine the sperate parts of the names from the cached rowset
    while (authorList.next()) {
        ID = authorList.getInt(1);
        authorFirstName = authorList.getString(2);
        authorMiddleName = authorList.getString(3);
        authorLastName = authorList.getString(4);

        if (authorMiddleName == null) {
            authorFullName = (authorFirstName + " " + authorLastName);
        } else {
            authorFullName = (authorFirstName + " " + authorMiddleName + " " + authorLastName);
        }

        // Add the authors to the hashmap and list
        bookAttributes.bidiMapAuthors.put(ID, authorFullName);
        bookAttributes.authors.add(authorFullName);
    }

    // Add the list to the choiceBox and auto-complete textfield
    choiceBoxAuthor.setItems(bookAttributes.authors);
    TextFields.bindAutoCompletion(textFieldAuthor, bookAttributes.listAuthors);
}

这是 hashmap 和 Observable 列表的代码。

// Lists
public static ObservableList<String> FictionGenres = FXCollections.observableArrayList();
public static ObservableList<String> NonFictionGenres = FXCollections.observableArrayList();
public static ObservableList<String> blankList=FXCollections.observableArrayList();
public static ObservableList<String> authors = FXCollections.observableArrayList();
public static ObservableList<String> publishers = FXCollections.observableArrayList();
public static ObservableList<String> languages = FXCollections.observableArrayList();
public static ObservableList<String> series = FXCollections.observableArrayList();

// Hashmaps
public static BidiMap<Integer, String> bidiMapAuthors = new TreeBidiMap<>();
public static BidiMap<Integer, String> bidiMapPublishers = new TreeBidiMap<>();
public static BidiMap<Integer, String> bidiMapFictionGenres = new TreeBidiMap<>();
public static BidiMap<Integer, String> bidiMapNonFictionGenres = new TreeBidiMap<>();
public static BidiMap<Integer, String> bidiMapLanguages = new TreeBidiMap<>();
public static BidiMap<Integer, String> bidiMapSeries = new TreeBidiMap<>();

一个重要的注意事项是哈希图和列表是在单独的 class 中创建和存储的,因此它们可以在程序的 运行 时间内跨多个控制器访问。

代码本身并不复杂并且相当容易理解,但是 我想提请您注意的部分是 while 循环之后的两行代码。这两行将作者的名字添加到哈希映射和列表中。

bookAttributes.bidiMapAuthors.put(ID, authorFullName);
bookAttributes.authors.add(authorFullName);

测试

我的测试就是这样进行的。我在方法的不同部分放置了计时器,运行 多次 将速度变慢的原因缩小到我之前提到的两行代码。 应该是注意到我为该方法的每个其他部分获得的时间正是我所期望的。

然后我注释掉了将名称添加到哈希映射的行,问题仍然存在,但是一旦我将其反转并注释掉将名称添加到列表的行,问题立即消失并且 不管我运行这个方法多少次都没有回来。

bookAttributes.authors.add(authorFullName);

这里发生的事情是否有任何原因,或者是否有一个明显的错误被我忽略了,我只是个白痴?我很好奇,非常感谢任何信息!

免责声明 我现在承认我不是 100% 确定我遇到的问题的根本原因是什么,所以我的解决方案可能不适合你。我已经对此进行了测试,直到我几乎失明,我只能提供 80-90% 的确定性。

原因

最初的问题似乎是 while 循环正在写入另一个 class 中的静态可观察列表。由于静态循环在全局变量池中,这意味着每次写入列表所花费的时间都比正常情况要长一些。在你一次写几百次之前,这不是一个严重的性能问题。

旁注 这就是为什么我不完全确定这是否真的是问题的原因,因为哈希映射也是静态的并且在同一个外部 class 但它们并没有导致速度下降。在任何时候他们都没有引起任何问题。我唯一的猜测是 Observable 列表进行更改的方式与哈希映射的操作方式不同,尽管我几乎不了解列表如何工作以明确说明这一点。

解决方案

感谢@tgdavies 的建议,我通过在与方法相同的 class 中创建这些列表的实例解决了这个问题,让方法写入这些 classes 然后一次所有类似的方法都已完成,我使用 'addAll' 方法将临时列表的内容复制到更永久的列表中。此更改极大地减少了运行时间。

   ObservableList<String> tempAuthorList=FXCollections.observableArrayList();
   ObservableList<String> tempPublisherList=FXCollections.observableArrayList();
   ObservableList<String> tempFictionGenreList=FXCollections.observableArrayList();
   ObservableList<String> tempNonFictionGenreList=FXCollections.observableArrayList();
   ObservableList<String> tempSeriesList=FXCollections.observableArrayList();
   ObservableList<String> tempLanguageList=FXCollections.observableArrayList();
   ObservableList<String> blankList=FXCollections.observableArrayList();
        bookAttributes.obvListAuthors.addAll(tempAuthorList);
        bookAttributes.obvListPublishers.addAll(tempPublisherList);
        bookAttributes.obvListFictionGenres.addAll(tempFictionGenreList);
        bookAttributes.obvListNonFictionGenres.addAll(tempNonFictionGenreList); 
        bookAttributes.obvListLanguages.addAll(tempLanguageList);     
        bookAttributes.obvListSeries.addAll(tempSeriesList);

我做的另一件事是实施了一个更好的清理选择框内容的过程。这似乎也导致了轻微的放缓。我从另一位名叫@sprinter 的评论者那里得到了这个想法,一旦我这样做了,任何挥之不去的性能问题都消失了。

choiceBoxAuthor.setItems(null); -> choiceBoxAuthor.setItems(blankList);

这些修复在其他地方引起了一些需要修复的故障,我已经用各种新值以各种方式对此进行了测试,代码似乎 运行时间。从现在开始的任何事情都应该简单地修复错误!