【问题标题】:Best method for string pattern matching in Java if performance is a concern如果性能是一个问题,Java 中字符串模式匹配的最佳方法
【发布时间】:2011-09-03 10:33:33
【问题描述】:

您好,

假设您想测试一个字符串以查看它是否完全匹配,或者它是否与 _ 以及在 _ 后面附加的任意数量的字符匹配

有效匹配示例:

MyTestString
MyTestString_
MyTestString_1234

如果性能是一个大问题,您会调查哪些方法?目前我正在做以下事情:

if (String.equals(stringToMatch)) {
            // success
        } else {
            if (stringToMatch.contains(stringToMatch + "_")) {
                // success
            }
            // fail
        }

我尝试用 _* 上的 Java.util.regex.Pattern 匹配替换 String.contains _ 的模式,但效果要差得多。我的解决方案是理想的还是您能想出更巧妙的方法来进一步提高性能?

感谢您的意见

【问题讨论】:

  • 顺便提一下:这个stringToMatch.contains(stringToMatch + "_") 将始终计算为false,因为应该在第一个字符串中的字符串比那个长。
  • 发布您尝试过的确切模式。顺便说一句,我不会使用贪婪的。
  • 您的数据是什么样的? "abc MyTestString cde" 应该匹配吗? "MyTestStringFooBar" 是有效匹配吗?你有一个大的文本块吗?

标签: java regex string-comparison


【解决方案1】:

我运行了一些基准测试。这是我能得到的最快速度。

    String a = "Test123";
    String b = "Test123_321tseT_Test_rest";
    int len1 = a.length();
    int len2 = b.length();
    if ((len1 == len2 || (len2 > len1 && (b.charAt(len1)) == '_'))
        && b.startsWith(a)) {
        System.out.println("success");
    } else {
        System.out.println("Fail");
    }

这至少会以合理的性能正常工作。

编辑:我切换了_检查和startsWith检查,因为startsWith的_检查会更差。

Edit2:修复 StringIndexOutOfBoundsException。

Edit3:Peter Lawrey 是正确的,只调用 1 次 a.length() 可以节省时间。在我的情况下为 2.2%。 最新的基准测试显示我比 OP 快 88%,比 Peter Lawrey 的代码快 10%。

Edit4:我将所有 str.length() 替换为本地变量,并运行了十几个基准测试。现在基准测试的结果变得如此随机,以至于无法说出什么代码更快。我的最新版本似乎赢了一个档次。

【讨论】:

  • 我对 Peter Lawrey 的代码进行了基准测试,我的代码比他的快 9%。它也比 OP 中的代码快 85%。
【解决方案2】:

你可以这样做

if(string.startsWith(testString)) {
    int len = testString.length();
    if(string.length() == len || string.charAt(len) == '_')
          // success
}

我假设您希望 testString 出现,即使您有一个“_”?


编辑:关于是使用一个长条件还是嵌套 if 语句,代码或性能没有区别。

public static void nestedIf(boolean a, boolean b) {
    if (a) {
        if (b) {
            System.out.println("a && b");
        }
    }
}

public static void logicalConditionIf(boolean a, boolean b) {
    if (a && b) {
        System.out.println("a && b");
    }
}

编译成相同的代码。如果你这样做javap -c

public static void nestedIf(boolean, boolean);
  Code:
   0:   iload_0
   1:   ifeq    16
   4:   iload_1
   5:   ifeq    16
   8:   getstatic       #7; //Field java/lang/System.out:Ljava/io/PrintStream;
   11:  ldc     #8; //String a && b
   13:  invokevirtual   #9; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   16:  return

public static void logicalConditionIf(boolean, boolean);
  Code:
   0:   iload_0
   1:   ifeq    16
   4:   iload_1
   5:   ifeq    16
   8:   getstatic       #7; //Field java/lang/System.out:Ljava/io/PrintStream;
   11:  ldc     #8; //String a && b
   13:  invokevirtual   #9; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   16:  return

编译出来的代码是一样的。

【讨论】:

  • 把所有的检查放在同一个“if”中会提高性能,如果这个花絮被大量调用的话。 (如在一个长循环中)
  • 使用嵌套的 if 或带有逻辑条件的单个 if 是一回事。使用嵌套的 if 可以避免在不匹配时检查 length()。
  • 此外,这听起来像是过早的优化。如果代码真的很热,我怀疑 JIT 在任何一种情况下都会做同样的事情。
  • 彼得,感谢您的出色回复,这是最有帮助的!
  • @Peter:谢谢。我刚刚遇到了一个 PHP 脚本案例,其中用单个“if”替换嵌套条件显着提高了性能(快了几秒钟),我认为这可能值得检查,因为 OP 似乎关心性能。您的测试清楚地表明,Java 并非如此。关于“length()”,如果条件的第一部分失败,它会被调用吗? @bkail:你是对的,但值得一试。
【解决方案3】:

您可以使用正则表达式来匹配模式。您可以使用stringToMatch.matches(".*?_.*?")。这将返回一个布尔值。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-29
    • 2021-04-25
    • 1970-01-01
    相关资源
    最近更新 更多