Java Scanner vs String.split() vs StringTokenizer; который я должен использовать?
В настоящее время я использую split()
для сканирования файла, где каждая строка имеет количество строк, разделенных символом '~'
. Я где-то читал, что Scanner
мог бы лучше работать с длинным файлом, по производительности, поэтому я подумал о его проверке.
Мой вопрос: должен ли я создать два экземпляра Scanner
? То есть читать одну строку и другую, основанную на строке, чтобы получить токены для разделителя? Если я должен это сделать, я сомневаюсь, что я получу какое-либо преимущество от его использования. Может быть, я что-то упустил?
Ответы
Ответ 1
Были ли некоторые метрики вокруг них в одной поточной модели, и вот результаты, которые я получил.
~~~~~~~~~~~~~~~~~~Time Metrics~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~ Tokenizer | String.Split() | while+SubString | Scanner | ScannerWithCompiledPattern ~
~ 4.0 ms | 5.1 ms | 1.2 ms | 0.5 ms | 0.1 ms ~
~ 4.4 ms | 4.8 ms | 1.1 ms | 0.1 ms | 0.1 ms ~
~ 3.5 ms | 4.7 ms | 1.2 ms | 0.1 ms | 0.1 ms ~
~ 3.5 ms | 4.7 ms | 1.1 ms | 0.1 ms | 0.1 ms ~
~ 3.5 ms | 4.7 ms | 1.1 ms | 0.1 ms | 0.1 ms ~
____________________________________________________________________________________________________________
Выходит, что сканер дает лучшую производительность, теперь то же самое нужно оценивать в многопоточном режиме! Один из моих старших говорит, что токенизатор дает процессорный всплеск, а String.split этого не делает.
Ответ 2
Для линии обработки вы можете использовать сканер и для получения токенов из каждой строки, вы можете использовать split.
Scanner scanner = new Scanner(new File(loc));
try {
while ( scanner.hasNextLine() ){
String[] tokens = scanner.nextLine().split("~");
// do the processing for tokens here
}
}
finally {
scanner.close();
}
Ответ 3
Вы можете использовать метод useDelimiter("~")
, чтобы вы могли выполнять итерацию по токенам в каждой строке с помощью hasNext()/next()
, продолжая использовать hasNextLine()/nextLine()
для итерации по самим линиям.
EDIT: если вы собираетесь выполнить сравнение производительности, вы должны предварительно скомпилировать регулярное выражение, когда вы выполните тест split():
Pattern splitRegex = Pattern.compile("~");
while ((line = bufferedReader.readLine()) != null)
{
String[] tokens = splitRegex.split(line);
// etc.
}
Если вы используете String#split(String regex)
, регулярное выражение будет перекомпилировано каждый раз. (Сканер автоматически кэширует все регулярные выражения при первом их компиляции.) Если вы это сделаете, я бы не ожидал увидеть большую разницу в производительности.
Ответ 4
Я бы сказал, что split()
является самым быстрым и, вероятно, достаточно хорошим для того, что вы делаете. Он менее гибкий, чем scanner
. StringTokenizer
устарел и доступен только для обратной совместимости, поэтому не используйте его.
EDIT: вы всегда можете проверить обе версии, чтобы узнать, какая из них быстрее. Мне любопытно, если scanner
может быть быстрее, чем split()
. Split может быть быстрее для заданного размера VS scanner
, но я не могу быть уверен в этом.
Ответ 5
На самом деле вам не нужно регулярное выражение, потому что вы разделяете фиксированную строку. Apache StringUtils
split разделяет на простые строки.
Для разделов большого объема, где разделение является узким местом, а не файлом IO, я обнаружил, что это будет в 10 раз быстрее, чем String.split()
. Тем не менее, я не тестировал его против скомпилированного регулярного выражения.
В Guava также есть сплиттер, реализованный более OO-способом, но я обнаружил, что он значительно медленнее, чем StringUtils для больших разделов.