【问题标题】:Fastest way to lookup a String value查找字符串值的最快方法
【发布时间】:2011-09-19 10:12:13
【问题描述】:

我有一个简单的应用程序,它从大文本文件中读取小字符串中的数据并将它们保存到数据库中。要实际保存每个这样的字符串,应用程序会调用以下方法数次(可能是数千次,甚至更多次):

setValue(String value)
{
    if (!ignore(value))
    {
         // Save the value in the database
    }
}

目前,我通过连续比较一组字符串来实现ignore()方法,例如

public boolean ignore(String value)
{
    if (value.equalsIgnoreCase("Value 1") || (value.equalsIgnoreCase("Value 2"))
    {
        return true;
    }

    return false;
}

但是,因为我需要检查许多这样的“可忽略”值,这些值将在代码的另一部分中定义,所以我需要使用数据结构进行此检查,而不是多个连续的 if 语句。

那么,我的问题是,从 标准 Java 到实现这一点的最快数据结构是什么?哈希表?一套?还有什么?

初始化时间不是问题,因为它会静态发生,每次应用程序调用一次。

编辑:到目前为止建议的解决方案(包括 HashSet)似乎比仅使用带有所有被忽略单词的 String[] 并仅针对每个单词运行“equalsIgnoreCase”要慢。

【问题讨论】:

    标签: java string data-structures comparison


    【解决方案1】:

    使用HashSet,以小写形式存储值,以及它的contains() 方法,该方法比TreeSet 具有更好的查找性能(包含常量时间与日志时间)。

    Set<String> ignored = new HashSet<String>();
    ignored.add("value 1"); // store in lowercase
    ignored.add("value 2"); // store in lowercase
    
    public boolean ignore(String value) {
        return ignored.contains(value.toLowerCase());    
    }
    

    以小写形式存储值并搜索小写输入避免了在比较过程中处理大小写的麻烦,因此您可以全速实现 HashSet 并编写零集合相关代码 (例如 Collat​​or、Comparator 等)。

    已编辑
    感谢 Jon Skeet 指出某些土耳其语字符在调用 toLowerCase() 时表现异常,但如果您不打算支持土耳其语输入(或者可能是其他具有非标准大小写问题的语言),那么这种方法对您很有效.

    【讨论】:

    • 另一方面,这假设您对小写字母作为执行不区分大小写比较的一种方式感到满意,这并不总是正确的。
    • @Jon Skeet 不是吗?你是说有a.toLowerCase().equals(b.toLowerCase()) ^ a.equalsIgnoreCase(b)的情况吗?如果有,请给一个。对我来说,javadoc 似乎说它们是一样的。我很高兴在这里犯错,因为我会学到一些东西!
    • 土耳其语有些奇怪。如果a="ma\u0131l"b="MA\u0130L" 会出现问题。请参阅 moserware.com/2008/02/does-your-code-pass-turkey-test.html 了解更多有趣的信息(基于 .NET,但仍然相关)。
    • 如果值全部以小写形式存储,或者全部以大写形式存储,有什么区别吗?我猜是一样的。
    • @Bohemian:Jon 所说的一个例子是,"MAIL".toLowerCase() 将在土耳其语言环境中返回 "maıl",因此它不会像您或我所期望的那样与 "mail" 匹配。相反,"mail".toUpperCase() 将返回 "MAİL"
    【解决方案2】:

    在大多数情况下,我通常会以 HashSet&lt;String&gt; 开头 - 但由于您希望不区分大小写,因此会稍微困难一些。

    您可以尝试使用 TreeSet&lt;Object&gt; 和适当的 Collator 以不区分大小写。例如:

    Collator collator = Collator.getInstance(Locale.US);
    collator.setStrength(Collator.SECONDARY);
    
    TreeSet<Object> set = new TreeSet<Object>(collator);
    

    请注意,您不能创建TreeSet&lt;String&gt;,因为Collator 只能实现Comparator&lt;Object&gt;

    编辑:虽然上述版本仅适用于字符串,但 可能 创建 TreeSet&lt;CollationKey&gt; 会更快:

    Collator collator = Collator.getInstance(Locale.US);
    collator.setStrength(Collator.SECONDARY);
    
    TreeSet<CollationKey> set = new TreeSet<CollationKey>();
    for (String value : valuesToIgnore) {
        set.add(collator.getCollationKey(value));
    }
    

    然后:

    public boolean ignore(String value)
    {
        return set.contains(collator.getCollationKey(value));
    }
    

    很好有一种方法可以存储所有忽略值的排序规则键,然后在测试时避免创建新的排序规则键,但我不知道这样做的方法。

    【讨论】:

    • 这是否意味着参考集应该使用'Collat​​ionKeys'?
    • @panzerschrek:编辑——我没想到。
    【解决方案3】:

    将要忽略的单词添加到列表中,然后检查该单词是否在该列表中。

    这是动态的。

    【讨论】:

    • 这是非常慢的 O(n)。它随着时间的推移而增长,他说有成千上万的比较。
    【解决方案4】:

    如果使用 Java 7,这是一种快速的方法:

    public boolean ignore(String value) {
      switch(value.toLowerCase()) { // see comment Jon Skeet
        case "lowercased_ignore_value1":
        case "lowercased_ignore_value2":
          // etc
          return true;
        default:
          return false;
      }
    }
    

    【讨论】:

      【解决方案5】:

      似乎 String[] 比其他建议的方法略好(在性能方面),所以我将使用它。

      就是这样:

      public boolean ignore(String value)
      {
          for (String ignore:IGNORED_VALUES)
          {
              if (ignore.equalsIgnoreCase(value))
              {
                  return true;
              }
      
              return false;
          }
      

      IGNORED_VALUES 对象只是一个 String[],其中包含所有被忽略的值。

      【讨论】:

        猜你喜欢
        • 2018-07-26
        • 1970-01-01
        • 1970-01-01
        • 2014-02-09
        • 1970-01-01
        • 2016-10-05
        • 2015-01-14
        • 2012-09-09
        • 1970-01-01
        相关资源
        最近更新 更多