Pattern.split 比 String.split 慢
Pattern.split slower than String.split
有两种方法:
private static void normalSplit(String base){
base.split("\.");
}
private static final Pattern p = Pattern.compile("\.");
private static void patternSplit(String base){
//use the static field above
p.split(base);
}
我在主要方法中这样测试它们:
public static void main(String[] args) throws Exception{
long start = System.currentTimeMillis();
String longstr = "a.b.c.d.e.f.g.h.i.j";//use any long string you like
for(int i=0;i<300000;i++){
normalSplit(longstr);//switch to patternSplit to see the difference
}
System.out.println((System.currentTimeMillis()-start)/1000.0);
}
直觉上,我认为 as String.split
最终会调用 Pattern.compile.split
(经过大量额外工作)来做真正的事情。我可以提前构造Pattern对象(它是线程安全的),加速拆分。
但事实是,使用预先构建的 Pattern 比直接调用 String.split
慢得多。我在他们身上尝试了一个50个字符长的字符串(使用MyEclipse),直接调用只消耗了使用预先构造的Pattern对象的一半时间。
有人能告诉我为什么会这样吗?
我觉得这只能用JIT优化来解释,String.split内部确实是这样实现的:
Pattern.compile(regex).split(this, limit);
当它在 String.class 内时它工作得更快,但是当我在测试中使用相同的代码时:
for (int i = 0; i < 300000; i++) {
//base.split("\.");// switch to patternSplit to see the difference
//p.split(base);
Pattern.compile("\.").split(base, 0);
}
我得到的结果与 p.split(base)
相同
这可能取决于 Java 的实际实施。我使用的是 OpenJDK 7,在这里,String.split
确实调用了 Pattern.compile(regex).split(this, limit)
,但是 仅当 要分割的字符串 regex
是不止一个字符。
有关源代码,请参阅 here,第 2312 行。
public String[] split(String regex, int limit) {
/* fastpath if the regex is a
(1)one-char String and this character is not one of the
RegEx's meta characters ".$|()[{^?*+\", or
(2)two-char String and the first char is the backslash and
the second is not the ascii digit or ascii letter.
*/
char ch = 0;
if (((regex.count == 1 &&
// a bunch of other checks and lots of low-level code
return list.subList(0, resultSize).toArray(result);
}
return Pattern.compile(regex).split(this, limit);
}
当您按 "\."
拆分时,它使用的是 "fast path"。也就是说,如果您正在使用 OpenJDK。
这是 String.split
行为的变化,是在 Java 7
中做出的。这就是我们 have 在 7u40
:
public String[] split(String regex, int limit) {
/* fastpath if the regex is a
(1)one-char String and this character is not one of the
RegEx's meta characters ".$|()[{^?*+\", or
(2)two-char String and the first char is the backslash and
the second is not the ascii digit or ascii letter.
*/
char ch = 0;
if (((regex.value.length == 1 &&
".$|()[{^?*+\".indexOf(ch = regex.charAt(0)) == -1) ||
(regex.length() == 2 &&
regex.charAt(0) == '\' &&
(((ch = regex.charAt(1))-'0')|('9'-ch)) < 0 &&
((ch-'a')|('z'-ch)) < 0 &&
((ch-'A')|('Z'-ch)) < 0)) &&
(ch < Character.MIN_HIGH_SURROGATE ||
ch > Character.MAX_LOW_SURROGATE))
{
//do stuff
return list.subList(0, resultSize).toArray(result);
}
return Pattern.compile(regex).split(this, limit);
}
这就是我们 had 在 6-b14
public String[] split(String regex, int limit) {
return Pattern.compile(regex).split(this, limit);
}
有两种方法:
private static void normalSplit(String base){
base.split("\.");
}
private static final Pattern p = Pattern.compile("\.");
private static void patternSplit(String base){
//use the static field above
p.split(base);
}
我在主要方法中这样测试它们:
public static void main(String[] args) throws Exception{
long start = System.currentTimeMillis();
String longstr = "a.b.c.d.e.f.g.h.i.j";//use any long string you like
for(int i=0;i<300000;i++){
normalSplit(longstr);//switch to patternSplit to see the difference
}
System.out.println((System.currentTimeMillis()-start)/1000.0);
}
直觉上,我认为 as String.split
最终会调用 Pattern.compile.split
(经过大量额外工作)来做真正的事情。我可以提前构造Pattern对象(它是线程安全的),加速拆分。
但事实是,使用预先构建的 Pattern 比直接调用 String.split
慢得多。我在他们身上尝试了一个50个字符长的字符串(使用MyEclipse),直接调用只消耗了使用预先构造的Pattern对象的一半时间。
有人能告诉我为什么会这样吗?
我觉得这只能用JIT优化来解释,String.split内部确实是这样实现的:
Pattern.compile(regex).split(this, limit);
当它在 String.class 内时它工作得更快,但是当我在测试中使用相同的代码时:
for (int i = 0; i < 300000; i++) {
//base.split("\.");// switch to patternSplit to see the difference
//p.split(base);
Pattern.compile("\.").split(base, 0);
}
我得到的结果与 p.split(base)
这可能取决于 Java 的实际实施。我使用的是 OpenJDK 7,在这里,String.split
确实调用了 Pattern.compile(regex).split(this, limit)
,但是 仅当 要分割的字符串 regex
是不止一个字符。
有关源代码,请参阅 here,第 2312 行。
public String[] split(String regex, int limit) {
/* fastpath if the regex is a
(1)one-char String and this character is not one of the
RegEx's meta characters ".$|()[{^?*+\", or
(2)two-char String and the first char is the backslash and
the second is not the ascii digit or ascii letter.
*/
char ch = 0;
if (((regex.count == 1 &&
// a bunch of other checks and lots of low-level code
return list.subList(0, resultSize).toArray(result);
}
return Pattern.compile(regex).split(this, limit);
}
当您按 "\."
拆分时,它使用的是 "fast path"。也就是说,如果您正在使用 OpenJDK。
这是 String.split
行为的变化,是在 Java 7
中做出的。这就是我们 have 在 7u40
:
public String[] split(String regex, int limit) {
/* fastpath if the regex is a
(1)one-char String and this character is not one of the
RegEx's meta characters ".$|()[{^?*+\", or
(2)two-char String and the first char is the backslash and
the second is not the ascii digit or ascii letter.
*/
char ch = 0;
if (((regex.value.length == 1 &&
".$|()[{^?*+\".indexOf(ch = regex.charAt(0)) == -1) ||
(regex.length() == 2 &&
regex.charAt(0) == '\' &&
(((ch = regex.charAt(1))-'0')|('9'-ch)) < 0 &&
((ch-'a')|('z'-ch)) < 0 &&
((ch-'A')|('Z'-ch)) < 0)) &&
(ch < Character.MIN_HIGH_SURROGATE ||
ch > Character.MAX_LOW_SURROGATE))
{
//do stuff
return list.subList(0, resultSize).toArray(result);
}
return Pattern.compile(regex).split(this, limit);
}
这就是我们 had 在 6-b14
public String[] split(String regex, int limit) {
return Pattern.compile(regex).split(this, limit);
}