【问题标题】:Efficiently filter an ArrayList in Java/Android在 Java/Android 中有效地过滤 ArrayList
【发布时间】:2011-01-09 12:16:39
【问题描述】:

我正在开发一个 Android 应用程序(Android 1.6),但这可能是一个更普遍的 Java 问题。

我有一个包含大约 10,000 个对象的 ArrayList

对象包含 3 个字符串(firstName、middleName、lastName)。

在 Android 上,用户会看到一个“搜索框”,他们可以在其中通过输入部分名称来搜索特定的“对象”。

我有一个类(我称之为 Filterer),它在 10,000 个列表中搜索匹配的对象,然后将它们作为“子列表”返回。

搜索有点慢(尤其是在 Android 手机上),我确信我没有以最有效的方式进行搜索/过滤。

有人对如何加快搜索速度有任何建议吗?我的代码如下。一种可能是搜索辅助“masterList”,该辅助“masterList”已经包含小写并连接的每条信息……但可能还有其他方法可以改进此搜索,这也将有所帮助。

TIA!!

public void filterNames() {
  this.filteredList.clear();
  String sv = this.searchString.toString.trim().toLowerCase(); // search value
  for (int i = 0; i < this.masterList.size(); i++) {
    MyObject d = this.masterList.get(i);
    String fn = d.getFirstName().toString().toLowerCase();
    String mn = d.getMiddleName().toString().toLowerCase();
    String ln = d.getLastName().toString().toLowerCase();

    if (fn.indexOf(sv) >= 0 || 
        md.indexOf(sv) >= 0 || 
        ln.indexOf(sv) >= 0) {
      this.currentList.add(d);
    }
  }
}

【问题讨论】:

标签: java android filter arraylist


【解决方案1】:

是的,为每个循环迭代小写几个对象肯定很痛苦(加上可能多余的toString?),而且每次迭代调用list.size() 也是不好的做法——该值应该在循环之前缓存开始。

无论如何,如果您要处理这么多数据,是否有理由不使用 SQLite 数据库来存储和使用CursorAdapter 显示/过滤您的列表?

这将是实现这种大小的推荐方式。

【讨论】:

  • SQLite(或其他 SQL DBMS)真的有助于中缀搜索吗?它有特殊的索引吗?
  • 本地循环“大小”变量是 Java 的老妇人故事,很像声明方法“最终”。 JVM 将内联 size() 调用,您不会看到任何性能提升。
  • @Civil Disobedient:这适用于大多数 JVM,但不一定适用于 Android 设备上的 Dalvik VM。请参阅developer.android.com/intl/fr/guide/practices/design/… 了解更多信息。
  • 它对于过滤功能Android联系人应用程序(或者它是内容提供者?)来说已经足够好了。那可能是个看看的好地方。
【解决方案2】:

也许您可以用一些空间换取一些速度?为您的数据创建某种形式的索引?

例如:

  1. 为每个字符 (a-z) 创建一个列表,其中包含名称的一部分包含该字符的所有“MyObject”(注意特殊字符!)。对于每个条目,计算“MyObject”的数量
  2. 如果用户键入查询,则查找单个字符并仅搜索条目数量最少的列表。

当然,添加名称需要您将其添加到索引中。

【讨论】:

    【解决方案3】:

    可能为时已晚,但它对其他陷入相同问题的人有所帮助。

    Java 8 (2014) 在一行代码中使用流和 lambdas 解决了这个问题:

    使用Stream Api,您可以在不使用 for 循环的情况下过滤数据,并且可以使用更多功能。

    List<MyObject> mFilteredMyObjectList = mMyObjectList.stream()
        .filter(d -> d.getFirstName().toString().toLowerCase().indexOf(sv) >= 0 
        || d.getMiddleName().toString().toLowerCase().indexOf(sv) >= 0 
        || d.getLastName().toString().toLowerCase().indexOf(sv) >= 0).collect(Collectors.toList());
    

    有关更多信息,请参阅下面的链接,

    Link1 Link2

    【讨论】:

    • Stream Api 默认适用于 24 岁及以上。
    【解决方案4】:

    经过更多研究后,我发现Suffix Arrays 可以为您提供快速的答案。另请查看Suffix Trees 的维基百科条目,以获得更多深入的解释。
    Besdies 我同意answer above,您可能可以使用 SQL 数据库进行此类查询。对数据执行 Sql 查询可能是在没有后缀数组的情况下获得所需内容的最快方法之一。
    在不执行 SQL 的情况下加快速度的一件事是将 firstName、middleName、lastName 放入一个小写字符串,然后将其放入引用 Array 索引的新 Map 中。这样,您可以将搜索减少到仅 10.000 个 hashmap 字符串,而不必每次都使用小写字母。它可能会更快一些,但当然需要更多的内存。也许尝试用正则表达式做一些事情来加速匹配。
    另一种选择是用Lucene 之类的东西真正创建一个搜索索引,尽管我认为这在Android 设备上确实有点矫枉过正,但可以在纯Java 中工作,而且Lucene 中的中缀搜索也不是超高性能。

    【讨论】:

    • SQLite(或其他 SQL DBMS)真的有助于中缀搜索吗?它有特殊的索引吗?据我所知,标准 SQL 索引并非旨在进行快速中缀(包含)搜索。
    • 好吧,这绝对不是最快的方法,使用适当的全文索引会更快。但我相信在 SQL Lite 中进行查询比通过数组搜索要快
    • 1) AFAIK 全文搜索解决方案(Lucene 等)并非旨在加速中缀搜索。如果您知道它们是,请提供相关文章/文档章节的链接。 2)你的信念是基于什么?甚至 SQL 引擎也必须遍历所有项目(记录),就像遍历 arraylist 中的所有项目一样。这是因为涉及中缀搜索,如果它是更简单的搜索类型(前缀搜索、精确值搜索等) - 使用索引将在 SQL 上获得巨大收益。
    • @WildWezyr:是的,中缀搜索在时间上总是很昂贵,但 Lucene 支持它wiki.apache.org/lucene-java/… 它不会像 O(1) 那样,但它至少应该比发布的 Java 代码快. SQL 也是如此,因为 SQLite 在 Android 上本机运行(C 代码),我希望它比 dalvik 代码更快。如果您真的想全力以赴并尽可能快地搜索,您需要使用后缀树或后缀数组之类的东西。检查维基百科。
    • 1) 您引用的 lucene 的 faq 条目指出 lucene 仅支持它,但默认情况下它是关闭的,因为它太贵了。结论:lucene 不是中缀搜索的好解决方案。 2) 本机 C 代码应该 (?) 比 Java 代码更快 - 但并不总是如此,对于这种特殊情况可能并非如此。也许值得一试(代码修改以使用 SQL DB 而不是 ArrayList),但不确定它是否真的会有所帮助。 3)首先我会尝试优化现有代码(例如摆脱小写等)。
    【解决方案5】:

    您最初是如何检索超过 10,000 个列表的?如果您只是使用instance of SQLite,我真的,强烈建议您在 SQL 中执行此操作。

    【讨论】:

    • SQLite(或其他 SQL DBMS)真的有助于中缀搜索吗?它有特殊的索引吗?据我所知,标准 SQL 索引并非旨在进行快速中缀(包含)搜索。
    猜你喜欢
    • 2023-03-27
    • 1970-01-01
    • 2020-03-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-18
    • 1970-01-01
    • 2017-10-27
    相关资源
    最近更新 更多