@DDwrt100

Как оптимизировать код Java при работе со строчками?

Добрый день, есть код, который парсит лог и нормализует его.
for (String a : rawToMassive) {
            if (a.contains("Var1:")) {
                a = a.replace("^Var1:", condition + ",");
                a = a + System.lineSeparator();
                out.append(a);

            }


Мне не нравиться, что при выполнении if , у меня дважды пересоздается объект String. Можно ли как то его оптимизировать ?
  • Вопрос задан
  • 69 просмотров
Решения вопроса 2
zagayevskiy
@zagayevskiy Куратор тега Java
Android developer at Yandex
да, использовать StringBuilder
Ответ написан
@Asapin
В свободное время ковыряюсь с Rust и Wasm
Про кеширование
Если у вас condition бОльшую часть времени имеет малый набор возможных значений, то кешированием в данном случае будет создание хешмапы, где ключ - condition, значение - condition + ",". Можете даже использовать какой-нибудь LRU-кеш, что бы редко используемые ключи постепенно удалялись из кеша и не занимали память.

Но если честно, в данном случае операция condition + "," настолько незначительна по вычислительной сложности, что хоть какое-то изменение вы заметите только если у вас этот участок кода вызывается в горячем цикле и генерирует столько мусора, что паузы GC перевешивают дополнительные вычисления хеша, ветвления и добавление новых данных в кеш. Но даже в этом случае нужно профилировать, например с помощью JMH и VisualVM.

Теперь по поводу StringBuilder.
Сразу скажу, что у меня нет под рукой IDE для Java, что бы проверить то, что будет написано дальше, все выводы сделаны только глядя на исходный код классов String и StringBuilder. Итак, поехали:
  • Код condition + "," будет преобразован компилятором в
    new StringBuilder().append(condition).append(",").toString()
    Это сразу +2 созданных объекта: StringBuilder и новый String, который будет создан в результате вызова метода StringBuilder.toString()

  • a = a.replace("^Var1:", newString); внутри себя тоже создаёт StringBuilder, у которого потом вызывается метод toString() - еще +2 объекта

  • a = a + System.lineSeparator(); опять же будет преобразован в StringBuilder + toString()


Итого, у вас создаётся минимум 6 объектов.

Если переменная condition может иметь произвольное значение (т.е. операции с ней нет смысла кешировать, так как всё-равно придётся каждый раз вычислять новое значение), то можете попробовать воспользоваться методом condition.concat(String str) - будете создавать только 1 объект вместо 2. Будет ли это быстрее? Не знаю, надо профилировать.
Далее, после входа в блок if создаём новый StringBuilder вместимостью a.length() + condition.length() + 2. Если не указать длину строки, то вместимость у StringBuilder будет либо 16 символов, либо равной длине значения в переменной a (в зависимости от того, какой конструктор будет использован). И при конкатенации с новыми строками скорее всего возникнет необходимость увеличивать размер буфера, что приведёт к дополнительному копированию массивов (создать буфер с новым размером, скопировать содержимое старого буфера в новый). Поэтому желательно сразу рассчитать правильную длину результирующей строки. В любом случае, это тоже +1 объект.
a.replace(CharSequence target, CharSequence replacement)
можно заменить методом
StringBuilder.replace(int start, int end, String str)
(придётся вручную вычислить начало и конец подстроки, которую вы хотите заменить).
Вместо a = a + System.lineSeparator(); используйте a.append(System.lineSeparator()).
out.append(a); приведёт к созданию еще 1 объекта - либо вы вручную вызовете a.toString(), либо этот метод будет вызван за вас в методе out.append(CharSequence seq). И скорее всего приведёт еще и к копированию массива символов внутри переменной out, если вы заранее не указали вместимость с запасом.

Итого, 3 объекта против изначальных 6 с несколькими не сразу очевидными НО. Стоит ли усложнение кода полученной экономии? Не уверен, надо профилировать. Ну и учтите, что производительность самого StringBuilder может быть разной, в зависимости от того, как вы его используете.

Имхо, то что вы пытаетесь сделать - это экономия на спичках, которая может помочь только если у вас по настоящему высоконагруженный сервис.
P.S. если вы пишете библиотеку, которая потом будет использоваться другими проектам, то я всеми конечностями за, если вы решили заморочиться с оптимизацией.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы