【问题标题】:Highest performance for finding substrings查找子字符串的最高性能
【发布时间】:2013-03-08 20:59:51
【问题描述】:

我有一个字符串数组(关键字),我需要检查有多少字符串存在于一个较大的字符串(从文件中读取的文本)中。我需要检查不区分大小写。 此刻我所做的是:

private void findKeywords() {
        String body = email.getMessage();
        for (String word : keywords) {
            if (body.toLowerCase().contains(word.toLowerCase())) {
                //some actions                }
            if (email.getSubject().contains(word)) {
                //some actions
            }
        }
    }

通过阅读此处的问题,出现了另一个解决方案:

private void findKeywords() {
        String body = email.getMessage();
        for (String word : keywords) {
            boolean body_match = Pattern.compile(Pattern.quote(word), Pattern.CASE_INSENSITIVE).matcher(body).find();
            boolean subject_match = Pattern.compile(Pattern.quote(word), Pattern.CASE_INSENSITIVE).matcher(email.getSubject()).find();
            if (body_match) {
                rating++;
            }
            if (subject_match) {
                rating++;
            }
        }
    }

以下哪种解决方案更有效?还有另一种更好的方法吗?任何公认的解决方案都必须易于实施(与上述方案相当)并且最好没有外部库,因为在这种情况下这不是很重要的问题。

【问题讨论】:

  • 为什么不尝试一下,看看哪个跑得最快???
  • 因为那时我不知道是否有比上面更快的解决方案。
  • 那你为什么不问是否有更快的解决方案?顺便说一句:有。使用 Lucene(或类似的)来索引您的电子邮件;索引存储比原始字符串搜索要快得多。
  • 我作为程序员的偏好是第一个代码块,因为它更容易立即发现它在做什么(因此更容易修改)。只有在确实需要时才应该调整算法以获得更好的性能......

标签: java string search


【解决方案1】:

这两种解决方案对我来说似乎都是可行的。我建议的一项改进是将函数移出循环。在您当前的代码中,您重复执行诸如 toLowerCase() 和 Pattern.compile 之类的操作,而您只需要执行一次。

显然有更快的方法来解决这个问题,但它们需要比这些 5 行代码复杂得多的代码。

【讨论】:

  • toLowerCase() 移到循环外不仅有性能优势,还可以减少一些可能存在的大量字符串重复。
【解决方案2】:

更好:构建包含所有关键字的单一模式。然后搜索该模式。假设您的关键字不包含元字符(模式中具有特殊含义的字符),则使用:

StringBuilder keywordRegex = new StringBuilder();
for (String w : keywords) {
   keywordRegex.append("|"+w);
}
Pattern p = Pattern.compile(keywordRegex.substring(1));
Matcher m = new p.matcher(textToMatch);
while (m.find()) {
    // match is at m.start(); word is m.group(0);
}

比遍历所有关键字更有效:模式编译(一次)将生成一个自动查找所有关键字的自动机。

【讨论】:

  • 您需要对每个关键字进行转义以确保稳健性,但是关键字越多,这种做法就会越完善。
【解决方案3】:

我认为您提到的显式正则表达式解决方案会更有效,因为它没有 toLowerCase 操作,该操作会将输入字符串复制到内存中并使字符小写。

这两种解决方案都应该是实用的,而且您的问题主要是学术性的,但我认为正则表达式提供了更简洁的代码。

【讨论】:

  • 我认为你对String.contains() 在下面使用正则表达式非常错误。你说的是哪个 JDK?
  • 我在想replaceAll
【解决方案4】:

如果您的电子邮件正文非常大,编写一个专门的不区分大小写的包含可能是合理的,因为您可以避免在大字符串上调用 toUpperCase():

static bool containsIgnoreCase(String big, String small) {
  if (small == null || big == null || small.length() > big.length()) {
    return false;
  }      
  String smallLC = small.toLowerCase();
  String smallUC = small.toUpperCase();
  for (int i = 0; i < big.length(); ++i) {
    if (matchesAt(big, i, smallLC, smallUC)) {
      return true;
    }
  }
  return false;
}

private static bool matchesAt(String big, int index, String lc, String uc) {
  if (index + lc.length() > big.length()) {
    return false;
  }
  for (int i = 0; i < lc.length(); ++i) {
    char c = big.charAt(i + index);
    if ((c != lc.charAt(i)) && (c != uc.charAt(i))) {
      return false;
    }
  }
  return true;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-03-11
    • 2021-07-02
    • 1970-01-01
    • 1970-01-01
    • 2011-05-07
    • 2016-07-01
    • 1970-01-01
    相关资源
    最近更新 更多