Java конкатенация для построения строки или формата

#java #string-formatting #string-concatenation #mud

#java #форматирование строки #строка-конкатенация #грязь

Вопрос:

В данный момент я пишу MUD (текстовую игру), используя Java. Одним из основных аспектов MUD является форматирование строк и отправка их обратно пользователю. Как это лучше всего выполнить?

Допустим, я хотел отправить следующую строку:

Вы говорите кому-то «Привет!» — где «Кто-то», «сказать» и «Привет!» — это все переменные. Что было бы наилучшим с точки зрения производительности?

"You " verb " to " user " "" text """

или

String.format("You %1$s to %2$s "%3$s"", verb, user, text)

или какой-либо другой вариант?

Я не уверен, что в конечном итоге будет проще в использовании (что важно, потому что это будет везде), но я думаю об этом на данный момент, потому что объединение с ‘s становится немного запутанным с некоторыми из больших строк. Я чувствую, что использование StringBuilder в этом случае просто сделает его еще менее читаемым.

Есть какие-либо предложения здесь?

Комментарии:

1. Не беспокойтесь о производительности, если вы не обнаружите, что это проблема и что это узкое место. Делайте все, что, по вашему мнению, приведет к наиболее читаемому и поддерживаемому коду.

Ответ №1:

Если строки построены с использованием одного выражения конкатенации; например

 String s = "You "   verb   " to "   user   " ""   text   """;
  

тогда это более или менее эквивалентно более длинному:

 StringBuilder sb = new StringBuilder();
sb.append("You");
sb.append(verb);
sb.append(" to ");
sb.append(user);
sb.append(" "");
sb.append(text );
sb.append('"');
String s = sb.toString();
  

Фактически, классический компилятор Java скомпилирует первое во второе … почти. В Java 9 они реализовали JEP 280, который заменяет последовательность вызовов конструктора и метода в байт-кодах одним invokedynamic байт-кодом. Затем система выполнения оптимизирует это1.

Проблемы с эффективностью возникают, когда вы начинаете создавать промежуточные строки или создавать строки с использованием = и так далее. На этом этапе StringBuilder становится более эффективным, потому что вы уменьшаете количество промежуточных строк, которые создаются, а затем выбрасываются.

Теперь, когда вы используете String.format() , он должен использовать StringBuilder под капотом. Однако format также необходимо анализировать строку формата каждый раз, когда вы выполняете вызов, и это накладные расходы, которых у вас нет, если вы выполняете построение строки оптимально.


Сказав это, я бы посоветовал написать код наиболее читаемым способом. Только беспокоиться о наиболее эффективном способе построения строк, если профилирование говорит вам, что это реальная проблема с производительностью. (Прямо сейчас вы тратите время на размышления о способах решения проблемы производительности, которая может оказаться незначительной или неактуальной.)

В другом ответе упоминается, что использование строки формата может упростить поддержку нескольких языков. Это верно, хотя существуют ограничения относительно того, что вы можете делать в отношении таких вещей, как множественное число, пол и так далее.


1 — Как следствие, ручная оптимизация в соответствии с приведенным выше примером может иметь негативные последствия для Java 9 или более поздней версии. Но это риск, на который вы идете всякий раз, когда выполняете микрооптимизацию.

Комментарии:

1. Спасибо за подробное объяснение, это помогло мне все продумать. Я собираюсь использовать String.format, поскольку это а) встроенное решение и б) уже упрощает мою жизнь, делая строки намного более удобочитаемыми.

Ответ №2:

Я думаю, что конкатенация с более удобочитаема, чем использование String.format .

String.format хорошо, когда вам нужно отформатировать число и даты.

Комментарии:

1. Кажется, это самый аккуратный и удобный выбор, и он реализован через java.util.Formatter .

Ответ №3:

Конкатенация с plus позволяет компилятору преобразовывать код перформативным способом. С форматом строки я не знаю.

Я предпочитаю объединять с плюсом, я думаю, что это легче понять.

Ответ №4:

Ключ к простоте — никогда не смотреть на это. Вот что я имею в виду:

 Joiner join = Joiner.on(" ");

public void constructMessage(StringBuilder sb, Iterable<String> words) {
  join.appendTo(sb, words);
}
  

Я использую класс Guava Joiner, чтобы сделать читаемость не проблемой. Что может быть понятнее, чем «присоединиться»? Все неприятные моменты, касающиеся конкатенации, хорошо скрыты. Используя Iterable, я могу использовать этот метод со всеми видами структур данных, списки являются наиболее очевидными.

Вот пример вызова с использованием Guava ImmutableList (который более эффективен, чем обычный список, поскольку любые методы, которые изменяют список, просто генерируют исключения и правильно представляют тот факт, что constructMessage() не может изменить список слов, просто использует его):

 StringBuilder outputMessage = new StringBuilder();
constructMessage(outputMessage, 
         new ImmutableList.Builder<String>()
            .add("You", verb, "to", user, """, text, """)
            .build());
  

Ответ №5:

Я буду честен и предлагаю вам выбрать первый, если вы хотите меньше печатать, или последний, если вы ищете способ сделать это в стиле C.

Я сидел здесь минуту или две, размышляя о том, что может быть проблемой, но я думаю, что все сводится к тому, сколько вы хотите ввести.

У кого-нибудь еще есть идея?

Комментарии:

1. Мои рассуждения об этом больше касаются удобочитаемости. Я довольно привык набирать «материал» «другой материал», и, вероятно, мне придется отказаться от этой привычки, если я выберу другой вариант. Изначально я думал о чем-то вроде «Вы [глагол] к [пользователю] [текст]» и выполняете replaceall (управляемый классом, поэтому я бы добавил переменные типа SetElement («глагол», verb), а затем он вернул бы форматированную строку), но наткнулся на .format и решил, что моя первоначальная идея будет ужасной с производительностью (но очень легко читается).

Ответ №6:

Предполагая, что вы собираетесь повторно использовать базовые строки, часто храните свои шаблоны как

String mystring = «Вам от $ 1 до $ 2 «$ 3 «»

Затем просто получите копию и замените $ X тем, что вы хотите.

Это было бы очень хорошо для файла ресурсов тоже.

Комментарии:

1. Также вы могли бы поддерживать несколько языков: download.oracle.com/javase/tutorial/i18n/resbundle/concept.html

Ответ №7:

Я думаю, String.format выглядит чище. Однако вы можете использовать StringBuilder и использовать функцию append для создания нужной строки

Ответ №8:

Лучшим с точки зрения производительности, вероятно, было бы использовать StringBuffer .

Комментарии:

1. Не согласен. Из StringBuilder javadoc: Там, где это возможно, рекомендуется использовать этот класс предпочтительнее, чем StringBuffer поскольку это будет быстрее в большинстве реализаций. — Кстати., я сомневаюсь, что конкатенация строк является узким местом в таком приложении.

2. Та же разница, один синхронизирован, другой нет. При правильной реализации синхронизации разница незначительна, если нет разногласий. (И, очевидно, не хотелось бы использовать StringBuilder, если есть конфликт.)