正则表达式的速度非常慢



前:我正试图使用regexp从一个大数组中提取不同类型的parts。该操作在AsyncTask中执行。part.plainname是一个字符串,最多256个字符。item_pattern看起来像"^keyword.*?$"

问题:我找到了一种方法,它减缓了一切:

public int defineItemAmount(NFItem[] parts, String item_pattern){
    System.out.println("STAMP2");
    int casecount = 0;
    for (NFItem part : parts) {
        if (testItem(part.plainname, item_pattern))
            ++casecount;
    }
    System.out.println("STAMP3");
    return casecount;
}
public boolean testItem(String testString, String item_pattern){
    Pattern p = Pattern.compile(item_pattern);
    Matcher m = p.matcher(testString);
    return m.matches();
}

只有950个parts,但它的工作速度非常慢:

02-25 11:34:51.773    1324-1343/com.nfe.unsert.dns_pc_creator I/System.out﹕ STAMP2
02-25 11:35:18.094    1324-1343/com.nfe.unsert.dns_pc_creator I/System.out﹕ STAMP3

20秒只是为了计数。testItem被大量使用,大约为15*parts。因此,整个应用程序的工作时间超过15分钟。而几乎相同的java程序(不适用于android应用程序)在不到30秒内完成。

问题:我做错了什么?为什么简单的regexp操作需要这么长时间?

您可以预编译模式:

public static int defineItemAmount(NFItem[] parts, String item_pattern){
    System.out.println("STAMP2");
    Pattern pattern = Pattern.compile(item_pattern);
    int casecount = 0;
    for (NFItem part : parts) {
        if (testItem(part.plainname, pattern))
            ++casecount;
    }
    System.out.println("STAMP3");
    return casecount;
}
public static boolean testItem(String testString, Pattern pattern){
    Matcher m = pattern.matcher(testString);
    return m.matches();
}

如果您正在寻找以关键字开头的字符串,则不需要将matches方法与这种模式^keyword.*?$:一起使用

  • 首先,非贪婪的量词是无用的,可能会让正则表达式引擎毫无进展,贪婪的量词会给你同样的结果
  • 由于默认情况下matches方法是锚定的,因此不需要锚定,因此可以删除它们
  • 您只对字符串的开头感兴趣,所以在这种情况下,lookingAt方法更合适,因为它不关心字符串末尾发生的事情
  • 正如其他答案所注意到的,如果多次使用相同的模式,请尝试在testItem函数之外一次性编译它。但如果不是这样,就不要编译它
  • 如果keyword是一个文本字符串而不是子模式,则根本不要使用regex,而是使用indexOf来检查关键字是否在索引0处

您不需要每次都编译模式。相反,在初始化时只做一次。

但是,由于它们的通用性,正则表达式的速度并不快,而且它们也不是为之设计的。如果数据足够规则,那么使用特定的字符串拆分技术可能会更好。

  1. 正则表达式通常,因为它们的构造涉及很多事情(如同步)。

  2. 不要在循环中调用单独的方法(这可能会阻止某些优化)。让VM优化for循环。使用这个并检查性能:

     Pattern p = Pattern.compile(item_pattern); // compile pattern only once
      for (NFItem part : parts) {
            if (testItem(part.plainname, item_pattern))
                ++casecount;
        }
      Matcher m = p.matcher(testString);
      boolean b = m.matches();
       ...
    

最新更新