【发布时间】:2019-01-07 20:11:46
【问题描述】:
这可能是一个实现细节,但对于 Oracle 和 IBM JDK,至少缓存了编译模式,还是我们作为应用程序开发人员需要自己执行编译模式的缓存?
【问题讨论】:
这可能是一个实现细节,但对于 Oracle 和 IBM JDK,至少缓存了编译模式,还是我们作为应用程序开发人员需要自己执行编译模式的缓存?
【问题讨论】:
据我查看代码 (JDK 6) 所知,它不进行缓存,但一旦构造,Pattern 对象可以缓存在应用程序端并在多个线程之间共享。标准模式似乎是将其分配给最终的静态变量:
private static final Pattern p = Pattern.compile(",");
【讨论】:
我不相信结果会被缓存,并且在code 或documentation 中没有此类行为的证据。自己实现这样的缓存(当然)会相对简单,但我会对这样的缓存有益的用例感兴趣。
回复。下面的评论和String.split(),有一种不同的方法,即代码为琐碎的 1 或 2 字符模式与更复杂的正则表达式采用不同的路径。但它似乎仍然没有缓存。
【讨论】:
我创建了一个类 CachedPattern,它可以缓存 Pattern 对象。 如果您运行 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;
}
}
【讨论】:
根据[Joshua_Bloch] Effective_Java:
有些对象的创建比其他的要昂贵得多。如果你要去 重复需要这样一个“昂贵的对象”,最好将它缓存起来以备不时之需 重用。不幸的是,当你创建这样一个 目的。假设你想写一个方法来判断一个字符串是否是 有效的罗马数字。这是使用常规执行此操作的最简单方法 表达式:
// Performance can be greatly improved!
static boolean 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 实例,并且只使用一次,之后 它变得有资格进行垃圾收集。创建 Pattern 实例 很昂贵,因为它需要将正则表达式编译成有限的 状态机。 为了提高性能,将正则表达式显式编译为 模式实例(不可变)作为类初始化的一部分,缓存它, 并为 isRomanNumeral 的每次调用重用相同的实例 方法:
// Reusing expensive object for improved performance
public class RomanNumerals {
private static final Pattern ROMAN = Pattern.compile(
"^(?=.)M*(C[MD]|D?C{0,3})"
+ "(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");
static boolean isRomanNumeral(String s) {
return ROMAN.matcher(s).matches();
}}
isRomanNumeral 的改进版本提供了显着的 如果频繁调用,性能会提高。在我的机器上,原始版本 8 个字符的输入字符串需要 1.1 μs,而改进的版本需要 0.17 μs,快了 6.5 倍
【讨论】:
它没有。如果您有性能敏感区域,您可能希望将模式对象保存为成员变量。
但是,当您在函数中有正则表达式时,Clojure 或多或少会自动执行此操作。
【讨论】: