Pattern.compile缓存吗?

时间:2012-11-16 16:14:50

标签: java regex

这可能是一个实现细节,但对于Oracle和IBM JDK,至少是缓存的编译模式还是应用程序开发人员需要自己执行编译模式的缓存?

5 个答案:

答案 0 :(得分:10)

据我所知,从查看代码(JDK 6)开始,它不进行缓存,但是一旦构造完成,Pattern对象可以缓存在应用程序端并在多个线程之间共享。标准模式似乎是将其分配给最终的静态变量:

private static final Pattern p = Pattern.compile(",");

答案 1 :(得分:5)

我不相信结果会被缓存,并且codedocumentation中没有此类行为的证据。 (当然)自己实现这样的缓存会是相对微不足道的,但我会对这种缓存有用的用例感兴趣。

重新。下面的注释和String.split(),有一种不同的方法,代码采用一个不同的路径来处理简单的1或2个char模式与更复杂的regexp。但它似乎仍然没有缓存。

答案 2 :(得分:4)

没有。如果您有性能敏感区域,则可能希望将模式对象保存为成员变量。

当你在函数中使用正则表达式时,Clojure会或多或少地自动执行此操作。

答案 3 :(得分:4)

我创建了一个可以缓存 Pattern 对象的 CachedPattern 类。 如果你运行 main 方法,你会发现Java的 Pattern 对象实际上是不同的实例,这也消耗了内存。

import java.util.HashMap;
import java.util.regex.Pattern;
import org.eclipse.core.runtime.Assert;

public class CachedPattern {

public static void main(String[] args){
    Pattern p1 = Pattern.compile("abc");
    Pattern p2 = Pattern.compile("abc");
    Pattern p3 = Pattern.compile("abc");
    Pattern p4 = Pattern.compile("abc");
    Pattern p5 = Pattern.compile("abc");

    Pattern x1 =  CachedPattern.compile("abc");
    Pattern x2 =  CachedPattern.compile("abc");
    Pattern x3 =  CachedPattern.compile("abc");
    Pattern x4 =  CachedPattern.compile("abc");
    Pattern x5 =  CachedPattern.compile("abc");
    // are cached objects the same ? YES!
    Assert.isTrue(x1.equals(x2));
    Assert.isTrue(x1.equals(x3));
    Assert.isTrue(x1.equals(x4));
    Assert.isTrue(x1.equals(x5));
    // are non-cached objects the same ? NO!
    Assert.isTrue(p1.equals(p2)); //AssertionFailedException
}

 private static HashMap<String, Pattern> cached = new HashMap<>();

 /**
  * This value must be unique, to make sure user won't use this inside "regex" variable,
  * so that objects without flags would be returned
  * For example if UNIQUE_HASH would be empty:
  *     compile(pattern = "abc1")
  *          VS.
  *     compile(pattern = "abc", flag = 1)
  * This would give same keys "abc1" and "abc1"
  */
 private static final String UNIQUE_HASH = "(())[]+@#$%^@!@#$%*";

 public static Pattern compile(String regex){
     if(cached.containsKey(regex)){
         return cached.get(regex);
     }
     Pattern p = Pattern.compile(regex);
     cached.put(regex, p);
     return p;
 }
 public static Pattern compile(String regex, int flags){
     String uniqueKey = regex + UNIQUE_HASH + flags;
     if(cached.containsKey(uniqueKey)){
         return cached.get(uniqueKey);
     }
     Pattern p = Pattern.compile(regex);
     cached.put(uniqueKey, p);
     return p;
 }

}

答案 4 :(得分:1)

根据 [Joshua_Bloch] Effective_Java

某些对象的创建要比其他对象昂贵得多。如果你要去 要反复需要这样的“昂贵对象”,建议将其缓存为 重用。不幸的是,当您创建这样一个 宾语。假设您要编写一种方法来确定字符串是否为 有效的罗马数字。这是使用常规方法最简单的方法 表达式:

  //性能可以大大提高!
静态布尔值isRomanNumeral(String s){
return s.matches(“ ^(?=。)M *(C [MD] | D?C {0,3})”
+“(X [CL] | L?X {0,3})(I [XV] | V?I {0,3})$”);
}
 

此实现的问题在于它依赖于 String.matches方法。虽然String.matches是最简单的方法 检查字符串是否与正则表达式匹配,不适合重复 在对性能有严格要求的情况下使用。问题是它在内部创建 正则表达式的Pattern实例,之后仅使用一次 它有资格进行垃圾收集。创建一个模式实例 之所以昂贵是因为它需要将正则表达式编译为有限 状态机。 为了提高性能,请将正则表达式显式编译为 模式实例(不可变)作为类初始化的一部分,将其缓存, 并为isRomanNumeral的每次调用重用同一实例 方法:

  //重用昂贵的对象以提高性能
公共类RomanNumerals {
私有静态最终模式ROMAN = Pattern.compile(
“ ^(?=。)M *(C [MD] | D?C {0,3})”
+“(X [CL] | L?X {0,3})(I [XV] | V?I {0,3})$”);
静态布尔值isRomanNumeral(String s){
返回ROMAN.matcher(s).matches();
}}
 

isRomanNumeral的改进版本提供了显着的 频繁调用可提高性能。在我的机器上,原始版本 8个字符的输入字符串花费1.1μs,而改进的版本花费 0.17μs,快6.5倍