正则表达式的效率比String.split更低吗?

fengyinxin 2014-07-06 11:21:29
今日手头的工作需要这么个函数:
接受两个字符串参数,其中第一个参数是以空格分隔的字符串,表示若干个值,要判断第一个参数的值中是否包含第二个参数
例如:
p1="AB CDE FG", p2="AB",此时应返回true
p1="AB CDE FG", p2="CDE",此时应返回true
p1="AB CDE FG", p2="CD",此时应返回false
好了,需求就是这样,很简单
解决方案也很简单:正则表达式。至少我以为很简单

写了一个函数

private void useRegexp(String s1, String s2) {
boolean found = Pattern.compile("(.* )*" + s2 + "( .*)*").matcher(s1).matches();
}

写完以后觉得有些担心:每次调用都要重新把Pattern拿来compile一次会不会性能低了?而由于每次传入的s2都不同,我又不可能把pattern弄成静态的
为了验证性能问题,又写了一个函数

private void useSplit(String s1, String s2) {
boolean found = Arrays.stream(s1.split(" ")).filter(s -> s.equals(s2)).findFirst().isPresent();
}

用同样的参数分别调用100w次,结果如下:
use useRegexp: 1270
use useSplit: 330
我就纳闷了,难道正则的性能比split还低?难道是每次compile带来的损耗?难道不是正则不行,是我不会用?
求解惑
...全文
1052 9 打赏 收藏 转发到动态 举报
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
hello123-hello123 2015-07-17
  • 打赏
  • 举报
回复
你试下 "(\\W" + s2 + " )|( " + s2 + "\\W)" 的效率
fengyinxin 2014-07-06
  • 打赏
  • 举报
回复
4楼的方案确实是比较复杂, 但是运行了一下,也确实非常快。100w次调用只要60ms 结贴了
zhjdg 2014-07-06
  • 打赏
  • 举报
回复
楼主,你搞的好复杂的。
shine333 2014-07-06
  • 打赏
  • 举报
回复
另外,实际上,class未必全是空格,制表符之类的,都可以
shine333 2014-07-06
  • 打赏
  • 举报
回复
性能的话,直接String操作更快:

  public static boolean useIndexOf(String s1, String s2) {
    int length1 = s1.length();
    int length2 = s2.length();
    int limit = length1 - length2;
    char last = ' ';
    for (int i = 0; i <= limit; i++) {
      if (last == ' ' // 左
          && (i == limit || s1.charAt(i + length2) == ' ') // 右
          && s1.regionMatches(i, s2, 0, length2)) {
        return true;
      }
      last = s1.charAt(i);
    }
    return false;
  }
fengyinxin 2014-07-06
  • 打赏
  • 举报
回复
这个函数是我用来解析html中节点的class属性时,加载到xpath库中的自定义函数 在我的系统中调用会非常频繁,预计每秒调用次数至少是好几千次 所以我必须十分关注性能
fengyinxin 2014-07-06
  • 打赏
  • 举报
回复
对啊,我也感觉split的实质也是正则,所以 不是正则不行,是我不会用 请问,这个函数的正则要如何写,才能达到不低于split的性能? 我猜测split的内部可能有一个缓存机制,我第一次调用split(" ")的时候,他就在内部把他的_pattern缓存起来,这样就不用每次都compile 猜的而已,如果是这样的话,这个机制我自己也可以实现,但是性能方面最多和split持平,而且显然不如直接调split来得方便 请问你怎么看?
MiceRice 2014-07-06
  • 打赏
  • 举报
回复
其实,split函数实现用的也是正则。。。 所以我认为是所写的正则计算的复杂度问题而已。 你的第一个正则表达式是"(.* )*" + s2 + "( .*)*",一堆的 星星(星号)和月亮(括号),必须快不起来。 第二个正则表达式是" ",只有一个空格,必须很快。
MiceRice 2014-07-06
  • 打赏
  • 举报
回复
引用 2 楼 fengyinxin 的回复:
请问你怎么看?
我的看法一楼已经写了,实际上就是你所编写正则的运算复杂度(与“我猜测split的内部可能有一个缓存机制”无关): —— 第一个正则表达式是"(.* )*" + s2 + "( .*)*",一堆的 星星(星号)和月亮(括号),必须快不起来。 —— 第二个正则表达式是" ",只有一个空格,必须很快。 通配符“.”和匹配搜索“*”都是很消耗计算量的,而且往往存在大量回溯操作(匹配失败后要回头再从第二个进行尝试),总的来说,可以认为是正则使用不当。

62,614

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧