Является ли java.util.Scanner медленным?
В приложении Android я хочу использовать класс Scanner для чтения списка поплавков из текстового файла (это список вершинных координат для OpenGL). Точный код:
Scanner in = new Scanner(new BufferedInputStream(getAssets().open("vertexes.off")));
final float[] vertexes = new float[nrVertexes];
for(int i=0;i<nrVertexFloats;i++){
vertexes[i] = in.nextFloat();
}
Кажется, что это невероятно медленно (потребовалось 30 минут, чтобы прочитать 10 000 поплавков!) - как проверено на эмуляторе 2.1. Что происходит?
Я не помню, чтобы Scanner был таким медленным, когда я использовал его на ПК (правда, я никогда раньше не читал более 100 значений). Или это что-то еще, например, чтение из потока ввода активов?
Спасибо за помощь!
Ответы
Ответ 1
Не знаю об Android, но, по крайней мере, на JavaSE, сканер работает медленно.
Внутри, сканер делает преобразование UTF-8, которое бесполезно в файле с поплавками.
Поскольку все, что вы хотите сделать, это читать поплавки из файла, вы должны пойти с пакетом java.io.
Ребята из SPOJ борются со скоростью ввода-вывода. Это сайт польского конкурса программистов с очень сложными проблемами. Их отличие состоит в том, что они принимают более широкий спектр языков программирования, чем другие сайты, и во многих своих проблемах вход настолько велик, что если вы не пишете эффективные операции ввода-вывода, ваша программа будет лопнуть срок.
Проверьте свои форумы, например здесь, для идеи пользовательского парсера.
Конечно, я советую не писать собственный флоатор-парсер, но если вам нужна скорость, это еще решение.
Ответ 2
Как и другие плакаты, более эффективно включать данные в двоичный формат. Однако для быстрого исправления я обнаружил, что замена:
scanner.nextFloat();
с
Float.parseFloat(scanner.next());
почти в 7 раз быстрее.
Чтобы добавить дополнительную информацию в этот ответ, источником проблем с производительностью с помощью метода является то, что он использует регулярное выражение для поиска следующего поплавка, что необязательно, если вы знаете структуру данных, которые вы читаете заранее.
В большинстве случаев (если не все) next*
используют обычные выражения по той же причине, поэтому, если вы знаете структуру своих данных, предпочтительнее всегда использовать next()
и анализировать результат. И.Е. также используйте Double.parseDouble(scanner.next())
и Integer.parseInt(scanner.next())
.
Соответствующий источник:
https://android.googlesource.com/platform/libcore/+/master/luni/src/main/java/java/util/Scanner.java
Ответ 3
Для Spotify Challenge они написали небольшую утилиту java для синтаксического анализа IO быстрее: http://spc10.contest.scrool.se/doc/javaio Утилита называется Kattio.java и использует BufferedReader, StringTokenizer и Integer.parseInt/Double.parseDouble/Long.parseLong для чтения чисел.
Ответ 4
Очень проницательный пост. Обычно, когда я работал с Java, мысль Scanner
была самой быстрой на ПК. То же самое, когда я пытаюсь использовать его в AsyncTask
на Android, его WORST.
Я думаю, что Android должен придумать альтернативу сканеру. Я использовал scanner.nextFloat();
и scanner.nextDouble();
и scanner.nextInt();
все вместе, что сделало мою жизнь больной. После того, как я проверил свое приложение, выяснилось, что виновник сидит скрытно.
Я изменил на Float.parseFloat(scanner.next());
аналогично Double.parseDouble(scanner.next());
и Integer.parseInt(scanner.next());
, что, безусловно, сделало мое приложение довольно быстрым, я должен согласиться, быстрее 60%.
Если кто-то испытал то же самое, напишите здесь. И я слишком смотрю на альтернативу API Scanner
, у любого есть яркие идеи, которые могут появляться и размещаться здесь при чтении форматов файлов.
Ответ 5
Да, я не вижу ничего подобного. Я могу читать около 10 М, плавающих таким образом через 4 секунды на рабочем столе, но это просто не может быть так.
Я пытаюсь думать о других объяснениях - возможно ли, что он блокирует чтение входного потока из getAssets()? Я мог бы попробовать полностью прочитать этот ресурс, выбрав время, а затем посмотрев, сколько времени потребуется для сканирования.
Ответ 6
Scanner
может быть частью проблемы, но вам нужно профилировать свой код, чтобы знать. Альтернативы могут быть быстрее. Вот простой тест, сравнивающий Scanner
и StreamTokenizer
.
Ответ 7
У меня точно такая же проблема. Потребовалось 10 минут, чтобы прочитать мой файл размером 18 КБ. В конце я написал настольное приложение, которое преобразует эти считываемые человеком числа в машиночитаемый формат, используя DataOutputStream.
Результат был поразительным.
Btw, когда я его отслеживал, большинство вызовов метода Scanner включает регулярные выражения, реализация которых обеспечивается пакетами com.ibm.icu.**
(проект IBM ICU). Это действительно излишне.
То же самое касается String.format. Избегайте его в Android!