Шпаргалка Java программиста 7.1 Типовые задачи: Оптимальный путь преобразования InputStream в строку
У меня есть хобби: я собираю различные решения типовых задач в Java, которые нахожу в инете, и пытаюсь выбрать наиболее оптимальное по размеру/производительности/элегантности. В первую очередь по производительности. Давайте рассмотрим такую типовую задач, которые часто встречаются в программировании на Java как «преобразование InputStream в строку» и разные варианты её решения.
Посмотрим какие ограничения есть у каждого (требования подключения определенной библиотеки/определенной версии, корректная работа с unicode и т.д.). Английскую версию этой статьи можно найти в моем ответе на stackoverflow. Тесты в моем проекте на github.
Преобразование InputStream в строку (String)
Очень часто встречающая задача, давайте рассмотрим какими способами можно это сделать (их будет 11):
Используя IOUtils.toString из библиотеки Apache Commons . Один из самых коротких однострочников.
Используя CharStreams из библиотеки guava . Тоже довольно короткий код.
Используя Scanner (JDK). Решение короткое, хитрое, с помощью чистого JDK, но это скорее хак, который вынесет мозг тем кто о таком фокусе не знает.
Используя Stream Api с помощью Java 8 . Предупреждение: Оно заменяет разные переносы строки (такие как \r\n ) на \n , иногда это может быть критично.
Используя parallel Stream Api ( Java 8 ). Предупреждение: Как и 4 решение, оно заменяет разные переносы строки (такие как \r\n ) на \n .
Используя InputStreamReader и StringBuilder из обычного JDK
Используя StringWriter и IOUtils.copy из Apache Commons
Используя ByteArrayOutputStream и inputStream.read из JDK
Используя BufferedReader из JDK . Предупреждение: Это решение заменяет разные переносы строк (такие как \n\r ) на line.separator system property (например, в Windows на «\r\n»).
Используя BufferedInputStream и ByteArrayOutputStream из JDK
Используя inputStream.read() и StringBuilder ( JDK ). Предупреждение: Это решение не работает с Unicode, например с русским текстом
Итак о использовании:
Решения 4 , 5 и 9 преобразую разные переносы строки в одну.
Решения 11 не работает с Unicode текстом
Замеры производительности
Предупреждение: замеры производительности всегда сильно зависят от системы, условий замера и т.п. Я измерял на двух разных компьютерах, один Windows 8.1, Intel i7-4790 CPU 3.60GHz2, 16Gb, второй — Linux Mint 17.2, Celeron Dual-Core T3500 2.10Ghz2, 6Gb, однако не могу гарантировать что результаты являются абсолютной истиной, вы всегда можете повторить тесты (test1 и test2) на вашей системе.
Замеры производительности для небольших строк (длина = 175), тесты можно найти на github (режим = среднее время выполнения (AverageTime), система = Linux Mint 17.2, Celeron Dual-Core T3500 2.10Ghz*2, 6Gb, чем значение ниже тем лучше, 1,343 — наилучшее):
Замеры производительности для больших строк (длина = 50100), тесты можно найти на github (режим = среднее время выполнения (AverageTime), система = Linux Mint 17.2, Celeron Dual-Core T3500 2.10Ghz*2, 6Gb, чем значение ниже тем лучше, 200,715 — наилучшее):
График зависимости среднего времени от длины строки, система Windows 8.1, Intel i7-4790 CPU 3.60GHz 3.60GHz, 16Gb:
Таблица зависимости среднего времени от длины строки, система Windows 8.1, Intel i7-4790 CPU 3.60GHz 3.60GHz, 16Gb:
Выводы
Самым быстрым решением во всех случаях и всех системах оказался 8 тест: Используя ByteArrayOutputStream и inputStream.read из JDK
Коротким и весьма быстрым решением будет использование IOUtils.toString из Apache Commons
Stream Api из Java 8 показывает среднее время, а использование параллельных стримов имеет смысл только при довольно большой строки, иначе он работает очень долго (что в общем-то было ожидаемо)
Источник