2013-09-25 3 views
5

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

Итак, мой вопрос в том, действительно ли они взаимозаменяемы при приращении цикла for, или есть какая-то непонятная проблема, с которой я буду сталкиваться в будущем, используя один порок другой?

Я решил пора их (внизу), и ++x определенно работает быстрее, чем x++, но я понятия не имею, почему. Может ли кто-нибудь расширить это?

Спасибо!

public class PlusPlus 
{ 
    public static void main(String[] args) 
    { 
     long startTime1, startTime2, endTime1, endTime2; 
     final double COUNT = 100000000; 

     //times x++ incrementing 
     startTime1 = System.currentTimeMillis(); 
     for(int x = 0; x < COUNT; x++); 
     endTime1 = System.currentTimeMillis(); 
     System.out.println("x++ loop: " + (endTime1 - startTime1) + " milliseconds"); 

     //times ++x incrementing 
     startTime2 = System.currentTimeMillis(); 
     for(int x = 0; x < COUNT; ++x); 
     endTime2 = System.currentTimeMillis(); 
     System.out.println("++x loop: " + (endTime2 - startTime2) + " milliseconds"); 
    } 
} 
+3

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

+2

Возможный дубликат [Is ++ x эффективнее, чем x ++ в Java?] (Http: // stackoverflow.com/questions/12396740/is-x-more-efficient-than-x-in-java) – hrv

+0

Основной вопрос был в том, что предпочтительнее приращивание цикла for, а не более эффективного. Я все еще очень новичок в программировании, и я постоянно сталкиваюсь с неясными ошибками, используя конкретный оператор или метод, который работает в одной ситуации, а не другой. Просто хочу убедиться, что я правильно использую его. Спасибо всем за ваши ответы. – brock

ответ

2

Для того, чтобы вернуть «старое» значение, оператор x++ должен сделать временную копию старого значения, в результате чего небольшое снижение производительности.

В вашем цикле for вы не используете возвращаемое значение, поэтому предпочтительнее ++x.

+2

Компилятор должен быть в состоянии сказать, что здесь не нужно и оптимизировать мертвый код. – Joel

3

Что касается эффективности, мы раскалываем волосы. Даже если вы действительно пытаетесь оптимизировать свое дело до металлического кода, x++ vs ++x isnt очень важно. Однако да, ++x технически быстрее, потому что x++ должен хранить старое значение x, увеличивать его, а затем возвращать старое значение. ++x не нужно его хранить.

Большая причина беспокоиться о ++x vs x++ - это действительно все о возвращаемом значении. Иногда вы хотите иметь один над другим.

1

++ x: increment x; значение полного выражения - это значение после приращения

x ++: increment x; значение всего выражения является значение до приращения

Рассмотрим эти две секции:

int x = 0; 
System.out.println(x++); // Prints 0 
// x is now 1 

int y = 0; 
System.out.println(++y); // Prints 1 
// y is now 1 

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

int x = 0; 
System.out.println(x); // Prints 0 
x++; 
// x is now 1 


int y = 0; 
y++; 
System.out.println(y); // Prints 1 
// y is now 1 

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

Есть определенные моменты, когда полезно иметь пред/пост-инкремент, доступный внутри выражения, но сначала думать о читаемости.

5

Ваш тест не даст многого с точки зрения понимания того, как работает работа. Из-за характера JVM писать тесты производительности достаточно сложно, чтобы все системы были написаны для бенчмаркинга.

Чтобы понять, в чем разница, вы должны декомпилировать код, используя javap.

Я составил этот класс:

public class MyClass { 

    public static void main(String[] args) { 
     for (int i = 0; i < 20; i++) 
      System.out.println(i); 
    } 
} 

И побежал javap -c MyClass, чтобы получить это для main:

public static void main(java.lang.String[]); 
    Code: 
    0: iconst_0 
    1: istore_1 
    2: iload_1 
    3: bipush 20 
    5: if_icmpge  21 
    8: getstatic  #2; //Field java/lang/System.out:Ljava/io/PrintStream; 
    11: iload_1 
    12: invokevirtual #3; //Method java/io/PrintStream.println:(I)V 
    15: iinc 1, 1 
    18: goto 2 
    21: return 

} 

я тогда сделал это снова, но на этот раз, использовали Преинкремент:

for (int i = 0; i < 20; ++i) 

И я получил это для javap:

public static void main(java.lang.String[]); 
    Code: 
    0: iconst_0 
    1: istore_1 
    2: iload_1 
    3: bipush 20 
    5: if_icmpge  21 
    8: getstatic  #2; //Field java/lang/System.out:Ljava/io/PrintStream; 
    11: iload_1 
    12: invokevirtual #3; //Method java/io/PrintStream.println:(I)V 
    15: iinc 1, 1 
    18: goto 2 
    21: return 

} 

Обратите внимание на что-нибудь? В частности, что они точно такие же?

Это связано с тем, что Java оптимизировала дополнительные инструкции по предварительной загрузке или после загрузки значения. Он распознает, что команда, которую он генерирует для загрузки переменной в регистр (iload_# в javap), является излишней и ненужной и оптимизирует ее.

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

Редактировать: Быстрое объяснение моего ответа: javac скомпилирует ваши .java-файлы в байт-код. Байт-код хранится в файлах .class. Байт-код сообщает JVM шаг за шагом, как выполнить что-то. Например, iinc 1, 1 сообщает, что он принимает переменную 1 (в данном случае это i) и увеличивайте ее на 1. iload_1 сообщает, что она принимает переменную 1 и загружает ее так, чтобы ее можно было использовать, как правило, при вызове операции или метода. Например, это:

8: getstatic  #2; //Field java/lang/System.out:Ljava/io/PrintStream; 
    11: iload_1 
    12: invokevirtual #3; //Method java/io/PrintStream.println:(I)V 

означает "получить System.out и загрузить его, а затем загрузить переменную # 1 (i), теперь вызвать метод # 3." Когда мы вызываем метод, первое, что загружается (System.out), является нашей «целью», что означает вызов метода для этого парня. Каждая другая вещь, загруженная в, передается в качестве аргумента. В этом случае это означает i. Таким образом, эти три строки обозначают линию System.out.println(i). Байт-код просто говорит Java, что нужно делать на самом деле do что.

Тот факт, что в декомпилированном коде 0-нет декомпилированного кода как для предварительного, так и для последующего приращения, означает, что Java оптимизировал его, так как он понял, что на самом деле он не будет использоваться. В обоих случаях он просто сделал iinc без iload, что в точности противоречит большинству других ответов в этой теме.

+0

Спасибо, Брайан, но все это на моей голове. Я все еще очень начинающий. – brock

+0

Кроме того, похоже, что вы использовали пост-инкремент для обоих декомпилиров, что могло бы объяснить, почему они оба выглядят одинаково. – brock

+0

@brock Это было просто ошибкой для копирования-вставки, извините. – Brian

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