Pattern.compile缓存吗?

Arc*_*ano 9 java regex

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

max*_*dim 10

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

private static final Pattern p = Pattern.compile(",");
Run Code Online (Sandbox Code Playgroud)


ibr*_*yel 6

根据[Joshua_Bloch] effective_Java

\n\n

有些对象的创建比其他对象要昂贵得多。如果您\xe2\x80\x99\n需要重复使用这样的\xe2\x80\x9c昂贵的对象\xe2\x80\x9d,建议将其缓存\n以供重复使用。不幸的是,当您创建这样的对象时,\xe2\x80\x99 并不总是显而易见的。假设您要编写一个方法来确定字符串是否为\n有效的罗马数字。这里\xe2\x80\x99是使用正则表达式执行此操作的最简单方法:

\n\n
// Performance can be greatly improved!\nstatic boolean isRomanNumeral(String s) {\nreturn s.matches("^(?=.)M*(C[MD]|D?C{0,3})"\n+ "(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

此实现的问题在于它依赖\nString.matches 方法。虽然 String.matches 是检查字符串是否与正则表达式匹配的最简单方法,但它不适合在性能关键情况下重复使用。问题在于它在内部为正则表达式创建\n模式实例并仅使用它一次,之后\n它就符合垃圾回收的条件。创建模式实例\n成本很高,因为它需要将正则表达式编译到有限状态机\n。\n为了提高性能,请将正则表达式显式编译到\n模式实例(不可变),作为类初始化的一部分,将其缓存,\n并为每次调用 isRomanNumeral\n方法重用相同的实例:

\n\n
// Reusing expensive object for improved performance\npublic class RomanNumerals {\nprivate static final Pattern ROMAN = Pattern.compile(\n"^(?=.)M*(C[MD]|D?C{0,3})"\n+ "(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");\nstatic boolean isRomanNumeral(String s) {\nreturn ROMAN.matcher(s).matches();\n}}\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果频繁调用,isRomanNumeral 的改进版本可提供显着\n的性能提升。在我的机器上,原始版本在 8 个字符的输入字符串上需要 1.1 \xce\xbcs,而改进版本需要\n0.17 \xce\xbcs,速度提高了 6.5 倍

\n


Bri*_*new 5

我不相信结果是缓存的,并且代码文档中没有这种行为的证据.(当然)自己实现这样的缓存会是相对微不足道的,但我会对这种缓存有益的用例感兴趣.

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


小智 5

我创建了一个可以缓存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;
 }

}
Run Code Online (Sandbox Code Playgroud)