2010-05-21 2 views
4

Это не должно быть субъективным, я ищу причины, основанные на использовании ресурсов, производительности компилятора, производительности GC и т. Д., А не на элегантности. О, и положение скобок не учитывается, поэтому никаких стилистических комментариев не нужно.Какой из следующих фрагментов кода Java лучше?

Возьмите следующий цикл;

Integer total = new Integer(0); 
Integer i; 
for (String str : string_list) 
{ 
    i = Integer.parse(str); 
    total += i; 
} 

по сравнению с ...

Integer total = 0; 
for (String str : string_list) 
{ 
    Integer i = Integer.parse(str); 
    total += i; 
} 

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

Есть довольно много других случаев, когда я склоняюсь к переменным сферы более широко, чем возможно, поэтому я думал, что попрошу здесь прояснить пробел в моих знаниях. Также обратите внимание, что назначение переменной при инициализации связано с новым оператором или нет. Разве какая-либо подобная полу-стилистическая полуоптимизация вообще не имеет значения?

+0

Я предполагаю, что ваш фактический цикл более сложный, чем ваш образец, и на самом деле делает использование i другим, чем временная переменная или что-то в этом роде. – BoltClock

+2

Почему вы используете дорогой Integer вместо int в первую очередь? – bmargulies

+2

Я предлагаю вам задать вопрос таким образом, чтобы сосредоточиться на том, что вы хотите узнать. Существует так много неэффективности в примере с бокс, unboxing и 'new' в Integer, что, безусловно, переполняет любую теоретическую микро-оптимизацию в отношении области действия переменной. – Yishai

ответ

7

Второй - это то, что я бы предпочел. Функциональной разницы нет, кроме области обзора.

Установка одной и той же переменной на каждой итерации не имеет значения, потому что Integer является неизменным классом. Теперь, если вы были , изменяя объект вместо того, чтобы создавать новый каждый раз, тогда будет разница.

И как примечание стороны, в этом коде вы должны использовать int и Integer.parseInt(), а не Integer и Integer.parse(). Вы вводите довольно много ненужного бокса и распаковки.


Edit: Это было некоторое время, так как я сброшенные вокруг в байткод, поэтому я думал, что мои руки грязные снова.

Вот тестовый класс я составил:

class ScopeTest { 
    public void outside(String[] args) { 
     Integer total = 0; 
     Integer i; 
     for (String str : args) 
     { 
      i = Integer.valueOf(str); 
      total += i; 
     } 
    } 
    public void inside(String[] args) { 
     Integer total = 0; 
     for (String str : args) 
     { 
      Integer i = Integer.valueOf(str); 
      total += i; 
     } 
    } 
} 

выход Bytecode (получен с javap -c ScopeTest после компиляции):

Compiled from "ScopeTest.java" 
class ScopeTest extends java.lang.Object{ 
ScopeTest(); 
    Code: 
    0: aload_0 
    1: invokespecial #1; //Method java/lang/Object."<init>":()V 
    4: return 

public void outside(java.lang.String[]); 
    Code: 
    0: iconst_0 
    1: invokestatic #2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 
    4: astore_2 
    5: aload_1 
    6: astore 4 
    8: aload 4 
    10: arraylength 
    11: istore 5 
    13: iconst_0 
    14: istore 6 
    16: iload 6 
    18: iload 5 
    20: if_icmpge  55 
    23: aload 4 
    25: iload 6 
    27: aaload 
    28: astore 7 
    30: aload 7 
    32: invokestatic #3; //Method java/lang/Integer.valueOf:(Ljava/lang/String;)Ljava/lang/Integer; 
    35: astore_3 
    36: aload_2 
    37: invokevirtual #4; //Method java/lang/Integer.intValue:()I 
    40: aload_3 
    41: invokevirtual #4; //Method java/lang/Integer.intValue:()I 
    44: iadd 
    45: invokestatic #2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 
    48: astore_2 
    49: iinc 6, 1 
    52: goto 16 
    55: return 

public void inside(java.lang.String[]); 
    Code: 
    0: iconst_0 
    1: invokestatic #2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 
    4: astore_2 
    5: aload_1 
    6: astore_3 
    7: aload_3 
    8: arraylength 
    9: istore 4 
    11: iconst_0 
    12: istore 5 
    14: iload 5 
    16: iload 4 
    18: if_icmpge  54 
    21: aload_3 
    22: iload 5 
    24: aaload 
    25: astore 6 
    27: aload 6 
    29: invokestatic #3; //Method java/lang/Integer.valueOf:(Ljava/lang/String;)Ljava/lang/Integer; 
    32: astore 7 
    34: aload_2 
    35: invokevirtual #4; //Method java/lang/Integer.intValue:()I 
    38: aload 7 
    40: invokevirtual #4; //Method java/lang/Integer.intValue:()I 
    43: iadd 
    44: invokestatic #2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 
    47: astore_2 
    48: iinc 5, 1 
    51: goto 14 
    54: return 

} 

Вопреки моим ожиданиям, была одна разница между ними: в outside() , переменная i по-прежнему занимала регистр, даже если она была опущена из фактического кода (обратите внимание, что все команды iload и istore указывают один регистр выше).

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

(Что касается моей предыдущей заметки, вы можете увидеть, что для добавления двух объектов Integer Java необходимо распаковать как с intValue, так и добавить новое целое число с valueOf. Не делайте этого, если абсолютно необходимо, потому что это бессмысленно и медленнее.)

+0

отличный ответ, спасибо. – Simon

+0

и как это выглядит, когда JIT делается с ним? – stacker

+0

Отличный ответ; дал бы вам +10, если бы мог. Особенно заметив расточительное использование бокса/распаковки - новую ловушку для Java-программ. –

0

Это не имеет никакого существенного различия кроме на последней итерации, когда задание очищается быстрее во втором примере (и это было бы мое предпочтение. - не так много, по этой причине, но ясности)

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

Я бы спросил, почему вы используете Integer вместо простого int ... или, может быть, это просто пример?

0

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

+0

стилистический, а не связанный с ресурсом – Simon

0

Ничего. Integer.valueOf(0); будет использовать ссылку на кешированный 0. :)

4

Второй вариант намного лучше, потому что первый стиль должен использоваться только в коде C как обязательный. Java позволяет встроенным объявлениям минимизировать область переменных, и вы должны воспользоваться этим. Но код может быть дополнительно улучшен:

int total = 0; 
for (String str: stringList) { 
    try { 
     total += Integer.valueOf(str); 
    } catch(NumberFormationException nfe) { 
     // more code to deal with the error 
    } 
} 

Это следует за соглашением стиля Java-кода. Прочитайте полное руководство здесь: http://java.sun.com/docs/codeconv/html/CodeConvTOC.doc.html

0

Ну, в этом случае, вы инстанцирование в Integer примитивного каждый раз, когда вы говорите i = Integer.parseInt(str) (где я являюсь Integer), поэтому (если Java не знает, как оптимизировать его), оба случая почти одинаково неэффективны. Рассмотрите возможность использования int вместо:

int total = 0; 
for (String str : string_list) 
{ 
    int i = Integer.parseInt(str); 
    total += i; 
} 

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

+0

спасибо, но вопрос в том, что * у * компилятор Java лижет приличную оптимизацию? – Simon

-1

Второй, поскольку вы хотите, чтобы область ваших переменных была как можно «внутренней». Преимущество меньшего объема меньше шансов для столкновения. В вашем примере есть всего несколько строк, поэтому преимущество может быть не столь очевидным. Но если он больше, то наличие переменных с меньшим охватом определенно более выгодно. Если кому-то позже придется смотреть на код, им придется сканировать весь путь назад вне определения метода, чтобы узнать, что такое i. Аргумент не сильно отличается от аргумента, почему мы хотим избежать глобальной переменной.

+0

это стилистический комментарий, я искал причины, основанные на языке и ресурсах, а не на ремонтопригодности и т. Д. – Simon

0

Второй вариант предпочтительнее двух для удобочитаемости, ремонтопригодности и эффективности.

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

Я предлагаю не использовать Integer для промежуточных значений. Накопите общее количество как int, а после цикла создайте объект или зависните от автоматического бокса.

+0

читабельность и ремонтопригодность стилистичны, и вы не оправдали эффективность остальной части своего ответа. – Simon

0

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

Я ищу причины, основанные на использования ресурсов, компилятор производительности, GC производительность и т.д. , а не элегантности.

Вы должны реализовать его самостоятельно нравится:

import java.util.ArrayList; 
import java.util.List; 

public class Int { 
    public static void main(String[] args) { 
     List<String> list = new ArrayList<String>(); 
     list.add("10"); 
     list.add("20"); 
     int total = 0; 
     for (String str : list) { 
      int val = 0; 
      for (char c : str.toCharArray()) { 
       val = val * 10 + (int) c - 48; 
      } 
      total += val; 
     } 
     System.out.print(total); 
    } 
} 

только GC соответствующая вещь будет toCharArray(), который может быть заменен на другой петлей с помощью charAt()

0

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

Теперь, если мы проверим технические последствия использования широких/узких областей применения, я считаю, что преимущество в производительности/скорости работы с узкими областями. Рассмотрим следующий метод, где мы имеем 3 локальные переменные, принадлежащие к одной глобальной области:

private static Random rnd = new Random(); 

public static void test() { 
    int x = rnd.nextInt(); 
    System.out.println(x); 

    int y = rnd.nextInt(); 
    System.out.println(y); 

    int z = rnd.nextInt(); 
    System.out.println(z);  
} 

Если вы diassemble этот код (с помощью javap -c -verbose {имя класса}, например), вы увидите, что компилятор резервов 3 слота для локальных переменных в структуре кадров стека метода test().

Теперь предположим, что мы добавим некоторые искусственные областей:

public static void test() { 
    { 
     int x = rnd.nextInt(); 
     System.out.println(x); 
    } 

    { 
     int y = rnd.nextInt(); 
     System.out.println(y); 
    } 

    { 
     int z = rnd.nextInt(); 
     System.out.println(z); 
    } 
} 

Если вы diassemble код сейчас, вы заметите, что компилятор оставляет только один слот для локальных переменных. Поскольку области полностью независимы, каждый раз, когда используются x, y или z, используется тот же слот # 0.

Что это значит?

1) Узкие прицелы сохранить пространство стека

2) Если мы имеем дело с переменным объектом, то это означает, что объекты могут стать недостижимыми быстрее, поэтому имеют право на GC раньше, чем в противном случае.

Опять же, обратите внимание, что эти 2 «преимущества» действительно незначительны, и озабоченность читабельностью должна быть, безусловно, самой важной задачей.

Смежные вопросы