Это то, как работает «Long» (в Java)?

#java

#java

Вопрос:

Посмотрите на этот Java-код:

 class PerformanceTest2{

    public static void main(String args[]){

        Long sum = 0L;

        for(int i=0;i<Integer.MAX_VALUE;i  )
            sum  = i;

        System.out.println("Sum = "   sum);
    }
} 
  

Замечено, что для этого кода требуется больше времени, поскольку сумма «Длинная», а не «длинная». Итак, на каждой итерации происходит следующее:

 sum = new Long(sum.longValue()   i); (for sum =i;)
  

Итак, каждый раз создается новый объект. Разве Java не поддерживает C -подобную функцию возврата ссылки, чтобы мы могли написать (возможно):

 sum.longValue()  = i;
  

возможно, не нужно создавать объект sum каждый раз в цикле? Я прав?

Ответ №1:

Ну, он не вызывает конструктор. Он использует:

 for (int i = 0; i < Integer.MAX_VALUE; i  )
{
   long tmp = sum.longValue(); // Unboxing
   tmp  = i;
   sum = Long.valueOf(tmp); // Boxing
}
  

Объекты-оболочки намеренно неизменяемы — их можно было бы легко спроектировать изменяемыми, но неизменяемость часто является очень полезной функцией. Если вы хотите написать свой собственный изменяемый тип оболочки, добро пожаловать — в этот момент у вас может быть такой код, как:

 LongWrapper sum = new LongWrapper(0L);
for (int i = 0; i < Integer.MAX_VALUE; i  )
{
    sum.add(i);
}
System.out.println("Sum = "   sum);
  

Или, возможно:

 LongWrapper sum = new LongWrapper(0L);
for (int i = 0;i < Integer.MAX_VALUE; i  )
{
    sum.setValue(sum.getValue()   i);
}
System.out.println("Sum = "   sum);
  

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

1. программа с «длинной» суммой выполнялась за 1.660 с, в то время как эта («Длинная»сумма) заняла 9.367с. Так может ли это быть причиной? И разве Java не создает sum каждый раз в цикле?

2. @AmitL: Да, бокс почти наверняка является причиной. Long.valueOf Метод будет вызываться на каждой итерации, но это не обязательно создает новое значение. Например, значения от -128 до 127 включительно гарантированно будут кэшироваться.

3. Пример кода очень быстро выйдет за пределы диапазона {-128, 127} . Я думаю, можно с уверенностью сказать, что почти на каждой итерации будет вызываться конструктор (посредством Long.valueOf() ). По крайней мере, на начальном проходе через цикл. И, вероятно, также при последующих проходах, поскольку маловероятно, что Java будет кэшировать где-либо рядом Integer.MAX_VALUE с экземплярами Long .

4. На самом деле я немного удивлен, что JIT не упрощает код соответствующим образом (должно быть выполнимо). Вероятно, потому, что это именно «неправильный путь». Обычно вы используете примитивы и позволяете компилятору автоматически привязывать его к объекту, если это необходимо.

5. @Voo: Не забывайте, что любая оптимизация, о которой должен знать JIT-компилятор, требует времени, пока он выполняет компиляцию JIT. Имеет больше смысла тратить это время на оптимизацию разумного кода, чем на то, чтобы понять, как глупый код можно заставить выглядеть не глупо.

Ответ №2:

Я приглашаю вас взглянуть на тестовые примеры, которые я создал здесь:

http://ideone.com/Hvbs1

Ваш код медленный не потому, что вы смешиваете long и int вводите, а потому, что вы используете Long вместо long . Long Тип является правильным объектом и неизменяемым при загрузке, поэтому каждый раз, когда вы присваиваете своей переменной новое значение, создается новый объект (возможное исключение — если кэшированный объект уже существует для нового значения). Это дорогостоящая операция (условно говоря).

Как вы увидите из примера кода, изменение цикла на добавление a long вместо an int не ускоряет его выполнение. Способ ускорить это — изменить первую переменную на a long вместо a Long .

Ответ №3:

В Java нет ссылок, подобных C . Кроме того, встроенные классы-оболочки для примитивных типов намеренно сделаны неизменяемыми. Одна из причин этого решения заключается в том, что во время выполнения может затем кэшировать экземпляры оболочки для определенных значений и избегать необходимости создавать новый объект (для этого требуется, чтобы вы вызывали valueOf вместо выделения нового объекта через new ; компилятор делает это для бокса).

Ответ №4:

Итак, каждый раз создается новый объект. Разве Java не поддерживает C -подобную функцию возврата ссылки, чтобы мы могли написать (возможно): …

Если вы используете Long , вы явно запрашиваете тип оболочки из Java. И соглашение для типов оболочек таково: они неизменяемы. И неизменность (как постоянство в C ) требует, чтобы никакие изменяемые внутренние элементы не передавались извне. Но ссылка, подобная C , точно сделает это. (Давайте пропустим часть ссылки на const, потому что это также не поможет вам в C .)

возможно, не нужно создавать объект sum каждый раз в цикле? Я прав?

Теоретически да, но если вы хотите такого поведения, почему бы вам не использовать not a plain long с самого начала?

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

1. итак, в данной программе создается сумма каждый раз в цикле? Надеюсь, что нет.

2. Конечно. Вместо этого вы просили Long «you l get it. It's that simple. If you don't want that, use long».

Ответ №5:

Другие уже объяснили, почему Long занимает больше времени long and how using , чем Long.valueOf` может быть немного быстрее.

Пожалуйста, не позволяйте этому быть причиной отказа от использования Long. По всей вероятности, это не повлияет на общее время пропускной способности вашей системы.

Если есть жесткие циклы, где это влияет на производительность, тогда используйте long там примитив, свернутую вручную оболочку, как описывает Джон, или MutableLong из apache commons.

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

1. На самом деле я не вижу никаких причин использовать «long» в этой ситуации. Благодаря автоматической блокировке мы все равно получаем объект, если это необходимо, и не имеем накладных расходов на производительность.

2. Если я правильно вас понимаю, вы говорите, что можете просто использовать, например, a Set<Long> и put a long в нем, и автобоксинг позаботится о преобразовании. Правильно, просто знайте, что автофиксация также будет преобразовываться с использованием valueOf (проверьте в javap) и будет иметь те же накладные расходы.

3. Ну, очевидно. Но компилятор будет автоматически блокировать примитив только тогда, когда это необходимо. Следовательно, мы получаем преимущества примитивов, когда это возможно, без каких-либо недостатков. Если мы сделаем это наоборот, мы столкнемся именно с теми проблемами производительности, которые мы видим здесь. До автоматической блокировки использование Long имело смысл — это помогло избежать дополнительных вызовов функций, но сегодня? Не вижу никаких преимуществ.