使用正则替代String.contains()导致渲染缓慢问题求助
解决正则替代多contains()后的超时问题
我之前踩过一模一样的坑!把一堆String.contains()换成正则后反而出现SocketTimeoutException,大概率是你写的正则存在重复扫描字符串的性能陷阱——尤其是当URL比较长的时候,这种问题会被放大。
先复盘下你的场景:原来的代码是判断URL转小写后不包含一堆特定关键词,大概是这样:
if (!url.toLowerCase().contains("wikipedia") && !url.toLowerCase().contains("vikidia") && !url.toLowerCase().contains("wikimini") && !url.toLowerCase().contains("dictionnaire") && !url.toLowerCase().contains("dictionary") // 还有更多类似条件 ) { // 你的业务逻辑 }
如果你的正则是写成这种多否定前瞻的形式,那性能差就不奇怪了:
// 这种写法会重复扫描字符串,效率极低! Pattern badPattern = Pattern.compile( "^(?!.*wikipedia)(?!.*vikidia)(?!.*wikimini)(?!.*dictionnaire)(?!.*dictionary)...", Pattern.CASE_INSENSITIVE ); if (badPattern.matcher(url).matches()) { ... }
每个(?!.*xxx)都要从字符串开头扫描到结尾,多个前瞻叠加起来,相当于把整个URL扫了N遍(N是关键词数量),长URL直接就会拖慢到超时。
给你两个靠谱的解决方案:
方案1:优化正则写法,单次扫描完成判断
把所有关键词合并到一个分组里,用|分隔,只做一次扫描判断是否存在任意关键词,然后反转逻辑即可:
// 初始化正则(建议放到静态常量里,避免重复编译) private static final Pattern EXCLUDED_PATTERN = Pattern.compile( "wikipedia|vikidia|wikimini|dictionnaire|dictionary|...", // 所有关键词用|分隔 Pattern.CASE_INSENSITIVE ); // 判断逻辑:只要找不到任何关键词,就执行业务代码 if (!EXCLUDED_PATTERN.matcher(url).find()) { // 你的业务逻辑 }
这个写法的优势是正则引擎只扫描URL一次,只要找到任意一个匹配的关键词就立即停止,找不到就返回false,性能比多前瞻写法提升N倍。
如果想进一步优化,可以提前把URL转成小写,去掉CASE_INSENSITIVE标志(这个标志会带来额外的性能开销):
String lowerUrl = url.toLowerCase(); if (!EXCLUDED_PATTERN.matcher(lowerUrl).find()) { ... }
方案2:用集合+Stream替代正则,简洁又稳定
如果正则还是让你不放心,不如回到集合的方式,代码同样简洁,而且性能更稳定(毕竟String.contains()是JDK底层优化过的字符串查找):
// 把所有关键词放到静态集合里(初始化一次即可) private static final Set<String> EXCLUDED_KEYWORDS = Set.of( "wikipedia", "vikidia", "wikimini", "dictionnaire", "dictionary" // 其他关键词 ); // 判断逻辑:检查URL转小写后是否不包含任何关键词 boolean isUrlAllowed = EXCLUDED_KEYWORDS.stream() .noneMatch(keyword -> url.toLowerCase().contains(keyword)); if (isUrlAllowed) { // 你的业务逻辑 }
这种方式的好处是:一旦找到匹配的关键词就会立即终止循环,不会多余扫描,而且代码可读性比正则更高,维护起来更方便。
最后提个小建议
如果你的URL长度经常很大(比如几KB以上),优先选方案2,因为正则引擎在处理超长字符串时,偶尔还是会出现性能波动;如果URL比较短,两种方案都可以,看你更喜欢哪种写法。
内容的提问来源于stack exchange,提问作者Dr. Mza
相关产品推荐
相关产品推荐

