В Java существует множество методов, которые все связаны с манипулированием строками. Самый простой пример - метод String.split( "что-то" ).
Теперь фактическое определение многих из этих методов заключается в том, что все они принимают регулярное выражение в качестве своего входного параметра (ов). Что делает тогда все очень мощные строительные блоки.
Теперь есть два эффекта, которые вы увидите во многих из этих методов:
- Они перекомпилируют выражение каждый раз при вызове метода. Таким образом, они влияют на производительность.
- Я обнаружил, что в большинстве ситуаций "реальной жизни" эти методы называются "фиксированными" текстами. Наиболее частое использование метода split еще хуже: обычно он вызывается с помощью одного char (обычно a ', a'; 'или' & ') для разделения.
Таким образом, это не только то, что методы по умолчанию мощные, они также кажутся подавленными для того, на что они фактически используются. Внутри мы разработали метод "fastSplit", который разбивается на фиксированные строки. Я написал тест дома, чтобы узнать, насколько быстрее я смог бы это сделать, если бы он был известен как один char. Оба они значительно быстрее, чем "стандартный" метод разделения.
Поэтому мне было интересно: почему Java API был выбран так, как сейчас? Какова была хорошая причина для этого, вместо того, чтобы иметь что-то вроде split (char) и split (String) и splitRegex (String)??
Обновление: я ударил несколько звонков, чтобы узнать, сколько времени будут иметь различные способы разделения строки.
Краткий обзор: он делает разницу большой!
Я сделал 10000000 итераций для каждого тестового примера, всегда используя вход
"aap,noot,mies,wim,zus,jet,teun"
и всегда использовать ',' или "," в качестве аргумента split.
Это то, что я получил в своей 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
Как вы можете видеть, это имеет большое значение, если у вас есть много "фиксированных char" разделов.
Чтобы дать вам, ребята, прозрение; В настоящее время я нахожусь в лог файлах Apache и арене Hadoop с данными большого веб-сайта. Так что мне этот материал действительно имеет значение:)
Что-то, что я не учитывал здесь, это сборщик мусора. Насколько я могу судить, компиляция регулярного выражения в Pattern/Matcher/.. будет выделять много объектов, которые нужно собрать некоторое время. Поэтому, возможно, в конечном итоге различия между этими версиями еще больше.... или меньше.
Мои выводы до сих пор:
- Только оптимизируйте это, если у вас есть много строк для разделения.
- Если вы используете методы regex, всегда прекомпилируйте, если вы повторно используете один и тот же шаблон.
- Забыть (устаревший) StringTokenizer
- Если вы хотите разделить на один char, используйте специальный метод, особенно если вам нужно только разбить его на определенное количество частей (например,... 2).
P.S. Я даю вам все мои родные раскол с помощью методов char, чтобы играть (по лицензии, что все на этом сайте попадает под:)). Я никогда их не тестировал. Получайте удовольствие.
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;
}