Java 带参数的Pattern.compile()

Java 带参数的Pattern.compile(),java,regex,Java,Regex,假设我有这个代码: public static boolean checkRegex(String regex, String value) { if (value == null) return false; Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(value); return matcher.matches();

假设我有这个代码:

public static boolean checkRegex(String regex, String value) {
        if (value == null) return false;
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(value);
        return matcher.matches();
    }
.......

checkRegex("^.+@.+\\..+$", "email@example.com");
我的正则表达式是在编译时编译一次还是在运行时编译多次?

模式。compile(regex)
只是一个对编译器没有特殊意义的方法,因此它只能在运行时执行。虽然可以缓存已编译的模式(这是用其他语言完成的,如Python),但Sun/Oracle的
模式
实现不会缓存已编译的
模式
,这意味着每次执行
checkRegex
方法时(即使使用相同的regex),重新编译正则表达式,得到一个新的
模式
实例

顺便说一下,你的方法

public static boolean checkRegex(String regex, String value) {
    if (value == null) return false;
    Pattern pattern = Pattern.compile(regex);
    Matcher matcher = pattern.matcher(value);
    return matcher.matches();
}
可以重写为

public static boolean checkRegex(String regex, String value) {
    if (value == null) 
        return false;
    return value.matches(regex);
}
Pattern.compile(regex)
只是一个对编译器没有特殊意义的方法,因此它只能在运行时执行。虽然可以缓存已编译的模式(这是用其他语言完成的,如Python),但Sun/Oracle的
模式
实现不会缓存已编译的
模式
,这意味着每次执行
checkRegex
方法时(即使使用相同的regex),重新编译正则表达式,得到一个新的
模式
实例

顺便说一下,你的方法

public static boolean checkRegex(String regex, String value) {
    if (value == null) return false;
    Pattern pattern = Pattern.compile(regex);
    Matcher matcher = pattern.matcher(value);
    return matcher.matches();
}
可以重写为

public static boolean checkRegex(String regex, String value) {
    if (value == null) 
        return false;
    return value.matches(regex);
}
简短回答 虽然从理论角度来看可能,不;不是在编译时,它们将再次编译

但是
Pattern
使用Flyweight模式,这样一旦编译了regex,它就会存储在内存中,因此运行时不需要进行完整编译

编译时 从理论上看,编译器有可能执行已知的操作,从而在编译时解决问题。如果您调用的方法是final(或者被调用方在编译时是已知的),则可以这样做

但是,如果有人编译您的方法并检查Java字节码,它将编译为:

public static boolean checkRegex(java.lang.String, java.lang.String);
    Code:
       0: aload_1       
       1: ifnonnull     6
       4: iconst_0      
       5: ireturn       
       6: aload_0       
       7: invokestatic  #15                 // Method java/util/regex/Pattern.compile:(Ljava/lang/String;)Ljava/util/regex/Pattern;
      10: astore_2      
      11: aload_2       
      12: aload_1       
      13: invokevirtual #16                 // Method java/util/regex/Pattern.matcher:(Ljava/lang/CharSequence;)Ljava/util/regex/Matcher;
      16: astore_3      
      17: aload_3       
      18: invokevirtual #17                 // Method java/util/regex/Matcher.matches:()Z
      21: ireturn       

  public static void main(java.lang.String[]);
    Code:
       0: ldc           #18                 // String ^.+@.+\..+$
       2: ldc           #19                 // String email@example.com
       4: invokestatic  #20                 // Method checkRegex:(Ljava/lang/String;Ljava/lang/String;)Z
       7: pop
       ...
正如您可能看到的,这些方法被简单地转换为Java字节码,并使用两个参数调用该方法

一些编译器(主要是函数和逻辑编程语言的编译器)允许程序专门化:如果某个调用是用常量完成的,编译器可以创建该方法的副本,并在编译时解析已知的内容。然而,这是一种折衷,因为引入大量的专门方法将减少大量代码

运行时的Flyweight模式 如果深入研究
Java.util.regex.Pattern
的Java字节码,您将看到:

private static final HashMap<String, CharPropertyFactory> map;

static CharProperty charPropertyFor(String name) {
        // <editor-fold defaultstate="collapsed" desc="Compiled Code">
        /* 0: getstatic     java/util/regex/Pattern$CharPropertyNames.map:Ljava/util/HashMap;
         * 3: aload_0
         * 4: invokevirtual java/util/HashMap.get:(Ljava/lang/Object;)Ljava/lang/Object;
         * 7: checkcast     java/util/regex/Pattern$CharPropertyNames$CharPropertyFactory
         * 10: astore_1
         * 11: aload_1
         * 12: ifnonnull     19
         * 15: aconst_null
         * 16: goto          23
         * 19: aload_1
         * 20: invokevirtual java/util/regex/Pattern$CharPropertyNames$CharPropertyFactory.make:()Ljava/util/regex/Pattern$CharProperty;
         * 23: areturn
         *  */
        // </editor-fold>
    }
私有静态最终哈希映射;
静态CharProperty charPropertyFor(字符串名称){
// 
/*0:getstaticjava/util/regex/Pattern$CharPropertyNames.map:Ljava/util/HashMap;
*3:aload_0
*4:invokevirtualjava/util/HashMap.get:(Ljava/lang/Object;)Ljava/lang/Object;
*7:checkcast java/util/regex/Pattern$CharPropertyNames$CharPropertyFactory
*10:astore_1
*11:aload_1
*12:ifnonnull 19
*15:aconst_null
*16:goto 23
*19:aload_1
*20:invokeVirtualJava/util/regex/Pattern$CharPropertyNames$CharPropertyFactory.make:()Ljava/util/regex/Pattern$CharProperty;
*23:轮到你了
*  */
// 
}
注意
HashMap
,这意味着
模式
存储正则表达式的片段及其相应的微DFA,这种行为称为。这意味着您只编译正则表达式一次。显然,您仍然需要执行查找,因此这不是免费的优化,但肯定会有所帮助。

简短回答 虽然从理论角度来看可能,不;不是在编译时,它们将再次编译

但是
Pattern
使用Flyweight模式,这样一旦编译了regex,它就会存储在内存中,因此运行时不需要进行完整编译

编译时 从理论上看,编译器有可能执行已知的操作,从而在编译时解决问题。如果您调用的方法是final(或者被调用方在编译时是已知的),则可以这样做

但是,如果有人编译您的方法并检查Java字节码,它将编译为:

public static boolean checkRegex(java.lang.String, java.lang.String);
    Code:
       0: aload_1       
       1: ifnonnull     6
       4: iconst_0      
       5: ireturn       
       6: aload_0       
       7: invokestatic  #15                 // Method java/util/regex/Pattern.compile:(Ljava/lang/String;)Ljava/util/regex/Pattern;
      10: astore_2      
      11: aload_2       
      12: aload_1       
      13: invokevirtual #16                 // Method java/util/regex/Pattern.matcher:(Ljava/lang/CharSequence;)Ljava/util/regex/Matcher;
      16: astore_3      
      17: aload_3       
      18: invokevirtual #17                 // Method java/util/regex/Matcher.matches:()Z
      21: ireturn       

  public static void main(java.lang.String[]);
    Code:
       0: ldc           #18                 // String ^.+@.+\..+$
       2: ldc           #19                 // String email@example.com
       4: invokestatic  #20                 // Method checkRegex:(Ljava/lang/String;Ljava/lang/String;)Z
       7: pop
       ...
正如您可能看到的,这些方法被简单地转换为Java字节码,并使用两个参数调用该方法

一些编译器(主要是函数和逻辑编程语言的编译器)允许程序专门化:如果某个调用是用常量完成的,编译器可以创建该方法的副本,并在编译时解析已知的内容。然而,这是一种折衷,因为引入大量的专门方法将减少大量代码

运行时的Flyweight模式 如果深入研究
Java.util.regex.Pattern
的Java字节码,您将看到:

private static final HashMap<String, CharPropertyFactory> map;

static CharProperty charPropertyFor(String name) {
        // <editor-fold defaultstate="collapsed" desc="Compiled Code">
        /* 0: getstatic     java/util/regex/Pattern$CharPropertyNames.map:Ljava/util/HashMap;
         * 3: aload_0
         * 4: invokevirtual java/util/HashMap.get:(Ljava/lang/Object;)Ljava/lang/Object;
         * 7: checkcast     java/util/regex/Pattern$CharPropertyNames$CharPropertyFactory
         * 10: astore_1
         * 11: aload_1
         * 12: ifnonnull     19
         * 15: aconst_null
         * 16: goto          23
         * 19: aload_1
         * 20: invokevirtual java/util/regex/Pattern$CharPropertyNames$CharPropertyFactory.make:()Ljava/util/regex/Pattern$CharProperty;
         * 23: areturn
         *  */
        // </editor-fold>
    }
私有静态最终哈希映射;
静态CharProperty charPropertyFor(字符串名称){
// 
/*0:getstaticjava/util/regex/Pattern$CharPropertyNames.map:Ljava/util/HashMap;
*3:aload_0
*4:invokevirtualjava/util/HashMap.get:(Ljava/lang/Object;)Ljava/lang/Object;
*7:checkcast java/util/regex/Pattern$CharPropertyNames$CharPropertyFactory
*10:astore_1
*11:aload_1
*12:ifnonnull 19
*15:aconst_null
*16:goto 23
*19:aload_1
*20:invokeVirtualJava/util/regex/Pattern$CharPropertyNames$CharPropertyFactory.make:()Ljava/util/regex/Pattern$CharProperty;
*23:轮到你了
*  */
// 
}
注意
HashMap
,这意味着
模式
存储正则表达式的片段及其相应的微DFA,这种行为称为。这意味着您只编译正则表达式一次。显然你会