Какова стоимость исполнения для назначения одного строкового значения с использованием +

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

String newString = "This is a really long long long long long" +
    " long long long long long long long long long long long long " +
    " long long long long long long long long long string for example.";

Как обрабатывает JVM или .Net компилятор и другие оптимизации. Будет ли она создавать одну строку? Или он будет создавать 1 строку, затем новую конкатенацию значения, а затем еще одну конкатенацию значений снова?

Это для моего собственного любопытства.

Ответ 1

Это гарантируется спецификацией С#, чтобы быть идентичным созданию строки в одном литерале, потому что это константа времени компиляции. Из раздела 7.18 спецификации С# 3:

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

(см. спецификацию для получения точной информации о "перечисленных выше требованиях":)

Спецификация языка Java указывает его в нижней части раздел 3.10.5:

Строки, вычисленные константой выражения (§15.28) вычисляются в компилировать время, а затем обрабатывать, как если бы они были литералами.

Ответ 2

В самом деле, в Java компилятор превратит String в константу.

class LongLongString
{
    public LongLongString()
    {
        String newString = "This is a really long long long long long" +
            " long long long long long long long long long long long long " +
            " long long long long long long long long long string for example.";
    }

    public static void main(String[] args)
    {
        new LongLongString();
    }
}

Скомпилирован в:

Compiled from "LongLongString.java"
class LongLongString extends java.lang.Object{
public LongLongString();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   ldc #2; //String This is a really long long long long long long long long long long long long long long long long long  long long long long long long long long long string for example.
   6:   astore_1
   7:   return

public static void main(java.lang.String[]);
  Code:
   0:   new #3; //class LongLongString
   3:   dup
   4:   invokespecial   #4; //Method "<init>":()V
   7:   pop
   8:   return

}

Как видно, одна строка загружается в строку 4, а не несколько экземпляров String, которые загружаются.

Изменить: Исходный файл был скомпилирован с использованием javac версии 1.6.0_06. Глядя на Спецификация языка Java, третье издание (и тот же раздел, упомянутый в Jon Skeet answer), мне не удалось найти ссылку на то, должен ли компилятор конкатенировать многострочный String в один String, поэтому это поведение, вероятно, зависит от реализации компилятора.

Ответ 3

Проверьте это для себя. В коде С# (эквивалент Java тоже будет работать):

string x = "A" + "B" + "C";
string y = "ABC";

bool same = object.ReferenceEquals(x, y); // true

Вы увидите, что результат true.

В стороне, вы увидите, что строка также интернирована в пуле строк выполнения:

bool interned = object.ReferenceEquals(x, string.Intern(x)); // true

Ответ 4

Нет компрометации производительности. Оптимизация компилятора объединит это в одну строку (по крайней мере, на Java).

Ответ 5

Насколько я помню, это не создаст несколько строк, только один.

Ответ 6

Эквивалентный .NET IL для дополнения ответа coobird:

Для кода С#:

string s = "This is a really long long long long long" +
    " long long long long long long long long long long long long " +
    " long long long long long long long long long string for example.";
Console.WriteLine(s);

Отладочная компиляция дает:

.method public hidebysig static void Main(string[] args) cil managed
{
  .custom instance void [mscorlib]System.STAThreadAttribute::.ctor()
  .maxstack 1
  .locals init (
      [0] string str)
  L_0000: ldstr "This is a really long long long long long long long long long long long long long long long long long  long long long long long long long long long string for example."
  L_0005: stloc.0 
  L_0006: ldloc.0 
  L_0007: call void [mscorlib]System.Console::WriteLine(string)
  L_000c: ret 
}

Итак, как вы можете видеть, это одна строка.

Ответ 7

Пока все строки являются постоянными (как в вашем примере), в Java (и я думаю, С#) компилятор преобразует это в одну строку.

Вы получаете только проблемы с производительностью с +, если вы объединяете много динамических строк, например, в цикле. В этом случае используйте StringBuilder или StringBuffer.

Ответ 8

Отказ от ответственности: это справедливо для Java. Я бы предположил, что это верно для С#

Не только javac создаст одну String, но JVM будет использовать одну String для всех остальных String, которые содержат один и тот же текст.

String a = "He" + "llo th"+ "ere";
String b = "Hell" + "o the"+ "re";
String c = "Hello" +" "+"there";
assert a == b; // these are the same String object.
assert a == c; // these are the same String object.

Примечание: они будут тем же объектом String во время выполнения, даже если они находятся в разных классах в разных JARS, скомпилированных разными компиляторами.