这里是我的应用程序当前代码:的Java分割字符串表演
String[] ids = str.split("/");
当分析应用程序,我注意到,非可忽略的时间都花在了分裂的字符串。
我还了解到split
实际上需要一个正则表达式,这在我这里是没用的。
所以我的问题是,我可以使用什么替代方法来优化字符串拆分?我看过StringUtils.split
,但速度更快吗?
(我会一直尝试和测试自己,但剖析我的应用程序需要花费大量的时间,所以如果有人已经知道了答案那是节省一些时间)
这里是我的应用程序当前代码:的Java分割字符串表演
String[] ids = str.split("/");
当分析应用程序,我注意到,非可忽略的时间都花在了分裂的字符串。
我还了解到split
实际上需要一个正则表达式,这在我这里是没用的。
所以我的问题是,我可以使用什么替代方法来优化字符串拆分?我看过StringUtils.split
,但速度更快吗?
(我会一直尝试和测试自己,但剖析我的应用程序需要花费大量的时间,所以如果有人已经知道了答案那是节省一些时间)
String.split(String)
如果您的模式只有一个字符长度,则不会创建正则表达式。当用单个字符分割时,它将使用相当高效的专用代码。在这种特殊情况下,StringTokenizer
速度并不快。
这是在OpenJDK7/OracleJDK7中引入的。 Here's a bug report和a commit。我做了一个simple benchmark here。
$ java -version
java version "1.8.0_20"
Java(TM) SE Runtime Environment (build 1.8.0_20-b26)
Java HotSpot(TM) 64-Bit Server VM (build 25.20-b23, mixed mode)
$ java Split
split_banthar: 1231
split_tskuzzy: 1464
split_tskuzzy2: 1742
string.split: 1291
StringTokenizer: 1517
StringTokenizer
快得多简单的解析像这样(我一段时间后做了一些基准测试,你会得到巨大的加速)。
StringTokenizer st = new StringTokenizer("1/2/3","/");
String[] arr = st.countTokens();
arr[0] = st.nextToken();
如果你想伊克出多一点的表现,你可以手动做得一样好:
String s = "1/2/3"
char[] c = s.toCharArray();
LinkedList<String> ll = new LinkedList<String>();
int index = 0;
for(int i=0;i<c.length;i++) {
if(c[i] == '/') {
ll.add(s.substring(index,i));
index = i+1;
}
}
String[] arr = ll.size();
Iterator<String> iter = ll.iterator();
index = 0;
for(index = 0; iter.hasNext(); index++)
arr[index++] = iter.next();
的StringTokenizer是传统类,它是由于兼容性原因而保留,尽管在新代码中不鼓励使用它。建议任何寻求此功能的人使用String或java.util.regex包的拆分方法。 –
仅仅因为这是遗产并不意味着它没有用。事实上,这个特殊的课程实际上对提高性能非常有用,所以我实际上反对这个“传统”标签。 – tskuzzy
“String”和“java.util.regex”包的拆分方法会导致使用正则表达式的巨大开销。 'StringTokenizer'不。 –
如果您可以使用第三方库,当您不需要它时,Guava'sSplitter
不会产生正则表达式的开销,并且作为一般规则非常快。 (披露:我有助于番石榴。)
Iterable<String> split = Splitter.on('/').split(string);
(另外,Splitter
是作为一项规则much more predictable比String.split
。)
这对我来说是一个非常显着的区别,同时在大文件中使用它。 –
这篇文章推荐不使用Iterable,即使是Guava的团队负责人也这样说... http://alexruiz.developerblogs.com/?p = 2519 – sirvon
番石榴具有Splitter这是更灵活,所述String.split()
方法,并且不(必然)使用正则表达式。 OTOH,String.split()
已经在Java 7中进行了优化,以避免如果分隔符是单个字符时使用正则表达式机制。因此Java 7中的性能应该类似。
噢好的我正在使用Java 5(不幸的是,无法更改那) –
字符串的拆分方法可能是更安全的选择。 As of at least java 6(尽管api参考是7)他们基本上说不鼓励使用StringTokenizer。他们的措辞在下面引用。
“StringTokenizer是一个遗留类,为了兼容性的原因,尽管在新代码中不鼓励使用它,但建议任何寻求该功能的人都使用String或java.util.regex包的split方法。“
的StringTokenizer比其他任何分裂法快,但得到记号赋予到带标记的字符串一起返回分隔符提高了像50%的性能。这是通过使用构造函数java.util.StringTokenizer.StringTokenizer(String str, String delim, boolean returnDelims)
来实现的。这里有一些关于此事的其他见解:Performance of StringTokenizer class vs. split method in Java
你可以自己编写分割函数,这将是最快的。 这里是证明它的链接,它的工作对我来说,通过6X优化我的代码
StringTokenizer - reading lines with integers
斯普利特:366ms 的IndexOf:50ms的 的StringTokenizer:89ms GuavaSplit:109ms IndexOf2(一些超级优化在对上述问题给出)解决方案:14MS CsvMapperSplit(按行映射行):326ms CsvMapperSplit_DOC(建立一个文档并映射一气呵成的所有行):177ms
看到,因为我是在大规模的工作,余吨它有助于提供更多的基准测试,包括一些我自己的实现(我分割空间,但这应该说明通常需要多长时间):
我正在使用426 MB文件, 2622761行。唯一的空格是普通空格(“”)和行(“\ n”)。
首先,我空格替换所有线路,以及基准解析一个阵容庞大:
.split(" ")
Cumulative time: 31.431366952 seconds
.split("\s")
Cumulative time: 52.948729489 seconds
splitStringChArray()
Cumulative time: 38.721338004 seconds
splitStringChList()
Cumulative time: 12.716065893 seconds
splitStringCodes()
Cumulative time: 1 minutes, 21.349029036000005 seconds
splitStringCharCodes()
Cumulative time: 23.459840685 seconds
StringTokenizer
Cumulative time: 1 minutes, 11.501686094999997 seconds
我然后用线(这意味着功能和环路一次做过很多次,而不是所有的基准分割线):
.split(" ")
Cumulative time: 3.809014174 seconds
.split("\s")
Cumulative time: 7.906730124 seconds
splitStringChArray()
Cumulative time: 4.06576739 seconds
splitStringChList()
Cumulative time: 2.857809996 seconds
Bonus: splitStringChList(), but creating a new StringBuilder every time (the average difference is actually more like .42 seconds):
Cumulative time: 3.82026621 seconds
splitStringCodes()
Cumulative time: 11.730249921 seconds
splitStringCharCodes()
Cumulative time: 6.995555826 seconds
StringTokenizer
Cumulative time: 4.500008172 seconds
下面是代码:
// Use a char array, and count the number of instances first.
public static String[] splitStringChArray(String str, StringBuilder sb) {
char[] strArray = str.toCharArray();
int count = 0;
for (char c : strArray) {
if (c == ' ') {
count++;
}
}
String[] splitArray = new String[count+1];
int i=0;
for (char c : strArray) {
if (c == ' ') {
splitArray[i] = sb.toString();
sb.delete(0, sb.length());
} else {
sb.append(c);
}
}
return splitArray;
}
// Use a char array but create an ArrayList, and don't count beforehand.
public static ArrayList<String> splitStringChList(String str, StringBuilder sb) {
ArrayList<String> words = new ArrayList<String>();
words.ensureCapacity(str.length()/5);
char[] strArray = str.toCharArray();
int i=0;
for (char c : strArray) {
if (c == ' ') {
words.add(sb.toString());
sb.delete(0, sb.length());
} else {
sb.append(c);
}
}
return words;
}
// Using an iterator through code points and returning an ArrayList.
public static ArrayList<String> splitStringCodes(String str) {
ArrayList<String> words = new ArrayList<String>();
words.ensureCapacity(str.length()/5);
IntStream is = str.codePoints();
OfInt it = is.iterator();
int cp;
StringBuilder sb = new StringBuilder();
while (it.hasNext()) {
cp = it.next();
if (cp == 32) {
words.add(sb.toString());
sb.delete(0, sb.length());
} else {
sb.append(cp);
}
}
return words;
}
// This one is for compatibility with supplementary or surrogate characters (by using Character.codePointAt())
public static ArrayList<String> splitStringCharCodes(String str, StringBuilder sb) {
char[] strArray = str.toCharArray();
ArrayList<String> words = new ArrayList<String>();
words.ensureCapacity(str.length()/5);
int cp;
int len = strArray.length;
for (int i=0; i<len; i++) {
cp = Character.codePointAt(strArray, i);
if (cp == ' ') {
words.add(sb.toString());
sb.delete(0, sb.length());
} else {
sb.append(cp);
}
}
return words;
}
这是怎么了我用的StringTokenizer:
StringTokenizer tokenizer = new StringTokenizer(file.getCurrentString());
words = new String[tokenizer.countTokens()];
int i = 0;
while (tokenizer.hasMoreTokens()) {
words[i] = tokenizer.nextToken();
i++;
}
使用Apache Commons Lang中»3.0的
StringUtils.splitByWholeSeparator("ab-!-cd-!-ef", "-!-") = ["ab", "cd", "ef"]
如果您需要非正则表达式拆分,并希望在String数组的结果,然后使用StringUtils的,我比较StringUtils.splitByWholeSeparator与番石榴的Splitter和Java的String分裂,发现StringUtils更快。
感谢这个基准。你的代码是“不公平的”,虽然StringTokenizer部分避免了创建一个List并将其转换为一个数组......尽管起点不错! –