为什么Java中的大多数字符串操作都基于正则表达式?
在Java中,有一堆方法都与操作字符串有关。最简单的例子是 String.split(“something”) 方法。
现在,许多这些方法的实际定义是,它们都采用正则表达式作为其输入参数。这使得所有非常强大的构建块。
现在,您可以在许多这些方法中看到两种效果:
- 每次调用该方法时,它们都会重新编译表达式。因此,它们会对性能产生影响。
- 我发现,在大多数“现实生活”情况下,这些方法都是用“固定”文本来调用的。拆分方法最常见的用法甚至更糟:它通常使用单个字符(通常是' ',';'或'&')来调用。
因此,不仅默认方法功能强大,而且它们的实际用途似乎也过强。在内部,我们开发了一种“fastSplit”方法,可以在固定字符串上进行拆分。我在家里写了一个测试,看看如果已知它是单个字符,我能做得快多快。两者都比“标准”拆分方法快得多。
所以我在想:为什么Java API被选择成现在的样子?这样做的好理由是什么,而不是像split(char)和split(String)和splitRegex(String)这样的东西?
更新:我组合了几个调用,看看拆分字符串的各种方法需要多少时间。
简短总结:它有很大的不同!
我为每个测试用例进行了10000000次迭代,始终使用输入
"aap,noot,mies,wim,zus,jet,teun"
并且始终使用 ',' 或 “,”作为拆分参数。
这是我在Linux系统上得到的(它是一个Atom D510盒子,所以它有点慢):
fastSplit STRING
Test 1 : 11405 milliseconds: Split in several pieces
Test 2 : 3018 milliseconds: Split in 2 pieces
Test 3 : 4396 milliseconds: Split in 3 pieces
homegrown fast splitter based on char
Test 4 : 9076 milliseconds: Split in several pieces
Test 5 : 2024 milliseconds: Split in 2 pieces
Test 6 : 2924 milliseconds: Split in 3 pieces
homegrown splitter based on char that always splits in 2 pieces
Test 7 : 1230 milliseconds: Split in 2 pieces
String.split(regex)
Test 8 : 32913 milliseconds: Split in several pieces
Test 9 : 30072 milliseconds: Split in 2 pieces
Test 10 : 31278 milliseconds: Split in 3 pieces
String.split(regex) using precompiled Pattern
Test 11 : 26138 milliseconds: Split in several pieces
Test 12 : 23612 milliseconds: Split in 2 pieces
Test 13 : 24654 milliseconds: Split in 3 pieces
StringTokenizer
Test 14 : 27616 milliseconds: Split in several pieces
Test 15 : 28121 milliseconds: Split in 2 pieces
Test 16 : 27739 milliseconds: Split in 3 pieces
如您所见,如果您有很多“固定字符”拆分要做,则会有很大的不同。
给你们一些见解;我目前在Apache日志文件和Hadoop领域,拥有一个大网站的数据。所以对我来说,这些东西真的很重要:)
我在这里没有考虑到的是垃圾收集器。据我所知,将正则表达式编译成模式/匹配器/.。会分配很多对象,需要收集一些时间。因此,也许从长远来看,这些版本之间的差异甚至更大....或更小。
到目前为止,我的结论是:
- 仅当要拆分大量字符串时,才可对此进行优化。
- 如果使用正则表达式方法,则在重复使用相同的模式时始终进行预编译。
- 忘记(过时的)StringTokenizer
- 如果要在单个字符上拆分,请使用自定义方法,特别是如果您只需要将其拆分为特定数量的部分(例如...2).
附言:我给你所有我自己种植的按字符方法分割的玩法(在许可证下,这个网站上的所有内容都属于:))。我从来没有完全测试过它们。。还。玩得愉快。
private static String[]
stringSplitChar(final String input,
final char separator) {
int pieces = 0;
// First we count how many pieces we will need to store ( = separators + 1 )
int position = 0;
do {
pieces++;
position = input.indexOf(separator, position + 1);
} while (position != -1);
// Then we allocate memory
final String[] result = new String[pieces];
// And start cutting and copying the pieces.
int previousposition = 0;
int currentposition = input.indexOf(separator);
int piece = 0;
final int lastpiece = pieces - 1;
while (piece < lastpiece) {
result[piece++] = input.substring(previousposition, currentposition);
previousposition = currentposition + 1;
currentposition = input.indexOf(separator, previousposition);
}
result[piece] = input.substring(previousposition);
return result;
}
private static String[]
stringSplitChar(final String input,
final char separator,
final int maxpieces) {
if (maxpieces <= 0) {
return stringSplitChar(input, separator);
}
int pieces = maxpieces;
// Then we allocate memory
final String[] result = new String[pieces];
// And start cutting and copying the pieces.
int previousposition = 0;
int currentposition = input.indexOf(separator);
int piece = 0;
final int lastpiece = pieces - 1;
while (currentposition != -1 && piece < lastpiece) {
result[piece++] = input.substring(previousposition, currentposition);
previousposition = currentposition + 1;
currentposition = input.indexOf(separator, previousposition);
}
result[piece] = input.substring(previousposition);
// All remaining array elements are uninitialized and assumed to be null
return result;
}
private static String[]
stringChop(final String input,
final char separator) {
String[] result;
// Find the separator.
final int separatorIndex = input.indexOf(separator);
if (separatorIndex == -1) {
result = new String[1];
result[0] = input;
}
else {
result = new String[2];
result[0] = input.substring(0, separatorIndex);
result[1] = input.substring(separatorIndex + 1);
}
return result;
}