В Java есть множество методов, которые все имеют отношение к манипулированию строками.Простейшим примером является метод String.split («что-то»).
Теперь фактическое определение многих из этих методов состоит в том, что все они принимают регулярное выражение в качестве входных параметров.Это делает все очень мощные строительные блоки.
Теперь есть два эффекта, которые вы увидите во многих из этих методов:
- Они перекомпилируют выражение при каждом вызове метода.Как таковые, они влияют на производительность.
- Я обнаружил, что в большинстве «реальных» ситуаций эти методы вызываются с помощью «фиксированных» текстов.Наиболее распространенное использование метода split еще хуже: он обычно вызывается с одним символом (обычно '', a ';' или '&') для деления на.
Так что этоМало того, что методы по умолчанию являются мощными, они также кажутся слишком сильными для того, для чего они фактически используются.Внутри мы разработали метод «fastSplit», который разбивает фиксированные строки.Я написал тест дома, чтобы увидеть, насколько быстрее я смогу это сделать, если известно, что это будет один символ.И то, и другое значительно быстрее, чем «стандартный» метод разделения.
Так что мне было интересно: почему 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
Как вы можете видеть, это имеет большое значение, если у вас есть много сплитов с «фиксированным символом».
Чтобы дать вам, ребята, некоторое понимание;В настоящее время я нахожусь на арене файлов Apache и Hadoop с данными большого веб-сайта.Так что для меня этот материал действительно важен:)
Что-то, чего я здесь не учел, это сборщик мусора.Насколько я могу сказать, компиляция регулярного выражения в Pattern / Matcher / .. выделит много объектов, которые нужно собрать некоторое время.Так что, возможно, в конечном итоге различия между этими версиями будут еще больше ... или меньше.
Мои выводы на данный момент:
- Оптимизируйте это, только если у вас МНОГОстроки для разделения.
- Если вы используете методы регулярных выражений, всегда прекомпилируйте, если вы неоднократно используете один и тот же шаблон.
- Забудьте (устаревший) StringTokenizer
- Если вы хотите разделить наодин символ, а затем используйте пользовательский метод, особенно если вам нужно только разбить его на определенное количество частей (например ... 2).
PS Я даю вам все мои доморощенные сплитметодами 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;
}