Döngü öncesi veya döngüdeki değişkenleri bildirme arasındaki fark nedir?


312

Ben her zaman, genel olarak, döngü içinde tekrar tekrar bir aksine, bir döngü önce bir atma değişkeni bildirmek herhangi bir (performans) fark yapar mı merak ettim? Bir (oldukça anlamsız) Java örnek:

a) döngü öncesi bildirim:

double intermediateResult;
for(int i=0; i < 1000; i++){
    intermediateResult = i;
    System.out.println(intermediateResult);
}

b) döngü içinde beyan (tekrar tekrar):

for(int i=0; i < 1000; i++){
    double intermediateResult = i;
    System.out.println(intermediateResult);
}

Hangisi daha iyi, a veya b ?

Tekrarlanan değişken bildiriminin (örnek b ) teoride daha fazla yük oluşturduğundan şüpheliyim , ancak derleyiciler önemli olmayacak kadar akıllıdır. Örnek b daha kompakt olma ve değişkenin kapsamını kullanıldığı yerle sınırlama avantajına sahiptir. Yine de, örnek göre kod eğilimindedir a .

Edit: Özellikle Java davası ile ilgileniyorum.


Bu, Android platformu için Java kodu yazarken önemlidir. Google, zaman açısından kritik olan kodun, for döngüsü dışında artan değişkenleri bildirmesini önerir; for döngüsü içinde sanki o ortamda her seferinde yeniden bildirir. Performans farkı pahalı algoritmalar için çok belirgindir.
AaronCarson

1
@AaronCarson, Google tarafından bu öneriye bağlantı sağlayabilir misiniz
Vitaly Zinchenko

Yanıtlar:


256

Hangisi daha iyi, a veya b ?

Performans açısından baktığınızda, bunu ölçmeniz gerekir. (Ve bence, bir farkı ölçebilirseniz, derleyici çok iyi değil).

Bakım açısından b daha iyidir. Değişkenleri aynı yerde, mümkün olan en dar kapsamda tanımlayın ve başlatın. Beyan ve başlatma arasında boşluk bırakmayın ve ihtiyaç duymadığınız ad alanlarını kirletmeyin.


5
Çift yerine, eğer String ile ilgileniyorsa, hala "b" durumu daha iyi?
Antoops

3
@Antoops - evet, b bildirilen değişkenin veri türü ile ilgisi olmayan nedenlerle daha iyidir. Dizeler için neden farklı olabilir?
Daniel Earwicker

215

A ve B örneklerinizi 100 milyon kez ilmek yaparak her biri 20 kez çalıştırdım. (JVM - 1.5.0)

A: ortalama yürütme süresi: .074 sn

B: ortalama yürütme süresi: .067 sn

Benim için sürpriz B biraz daha hızlıydı. Bilgisayarlar kadar hızlı şimdi bunu doğru bir şekilde ölçüp ölçemeyeceğinizi söylemek zor. Ben de A yolu kodlamak ama gerçekten önemli olmadığını söyleyebilirim.


12
Beni dövüyorsun, sonuçlarımı profilleme için göndermek üzereydim, az çok aynıydım ve evet şaşırtıcı bir şekilde B daha hızlıdır.
Mark Davidson

14
Çok sürpriz değil - değişken döngü için yerel olduğunda, her yinelemeden sonra korunması gerekmez, böylece bir kayıtta kalabilir.

142
Sadece OP'nin kendini test edebileceği bir fikir / teori değil, test etmek için +1 .
MGOwen

3
Dürüst olmak gerekirse, bunun yapılmasını istiyorum. Bu testi, neredeyse aynı kod parçasıyla (istatistikleri çalıştırmak isteyen herkesle paylaşmak isterim) 50.000.000-100.000.000 yineleme için makinemde yaklaşık 10 kez çalıştırdım. Cevaplar neredeyse her iki şekilde de eşit olarak bölündü, bu da gerçekten fazla olmayan 900 ms'lik bir marjla (50 milyondan fazla iterasyon). İlk düşüncem bunun "gürültü" olacağına rağmen, birazcık yalın olabilir. Bu çaba benim için tamamen akademik görünüyor (çoğu gerçek yaşam uygulamaları için) .. Yine de bir sonuç görmek isterim;) Herkes kabul ediyor mu?
javatarz

3
Kurulumu belgelemeden test sonuçlarını göstermek değersizdir. Bu, özellikle her iki kod parçasının aynı bayt kodu ürettiği bu durumda doğrudur, bu nedenle ölçülen herhangi bir fark, sadece yetersiz test koşullarının bir işaretidir.
Holger

66

Dile ve tam kullanıma bağlıdır. Örneğin, C # 1'de bir fark yaratmadı. C # 2'de, yerel değişken anonim bir yöntemle (veya C # 3'te lambda ifadesiyle) yakalanırsa, çok önemli bir fark yaratabilir.

Misal:

using System;
using System.Collections.Generic;

class Test
{
    static void Main()
    {
        List<Action> actions = new List<Action>();

        int outer;
        for (int i=0; i < 10; i++)
        {
            outer = i;
            int inner = i;
            actions.Add(() => Console.WriteLine("Inner={0}, Outer={1}", inner, outer));
        }

        foreach (Action action in actions)
        {
            action();
        }
    }
}

Çıktı:

Inner=0, Outer=9
Inner=1, Outer=9
Inner=2, Outer=9
Inner=3, Outer=9
Inner=4, Outer=9
Inner=5, Outer=9
Inner=6, Outer=9
Inner=7, Outer=9
Inner=8, Outer=9
Inner=9, Outer=9

Fark, tüm eylemlerin aynı outerdeğişkeni yakalamasıdır , ancak her birinin kendi ayrı innerdeğişkeni vardır.


3
örnek B'de (orijinal soru), her seferinde yeni bir değişken yaratıyor mu? yığının gözünde ne oluyor?
Royi Namir

@Jon, C # 1.0 bir hata mıydı? İdeal Outerolarak 9 olmamalı mı ?
nawfal

@nawfal: Ne demek istediğini bilmiyorum. Lambda ifadeleri 1,0 değildi ... ve Dış olan Ne hata demek 9.?
Jon Skeet

@nawfal: Demek istediğim, C # 1.0'da, bir döngü içindeki bir değişkeni bildirmek ve dışarıda bildirmek arasındaki farkı (her ikisinin de derlendiğini varsayarak) anlayabileceğiniz herhangi bir dil özelliği bulunmaması. Bu C # 2.0 değişti. Hata yok.
Jon Skeet

@JonSkeet Ah evet, şimdi anlıyorum, 1.0'da böyle değişkenleri kapatamadığınız gerçeğini tamamen göz ardı ettim, kötü! :)
nawfal

35

Aşağıda yazdıklarım ve .NET'te derledim.

double r0;
for (int i = 0; i < 1000; i++) {
    r0 = i*i;
    Console.WriteLine(r0);
}

for (int j = 0; j < 1000; j++) {
    double r1 = j*j;
    Console.WriteLine(r1);
}

Bu benim aldığım şeydir NET Reflektör zaman CIL koduna geri oluşturulur.

for (int i = 0; i < 0x3e8; i++)
{
    double r0 = i * i;
    Console.WriteLine(r0);
}
for (int j = 0; j < 0x3e8; j++)
{
    double r1 = j * j;
    Console.WriteLine(r1);
}

Yani her ikisi de derlemeden sonra tamamen aynı görünüyor. Yönetilen dillerde kod CL / bayt koduna ve yürütme sırasında makine diline dönüştürülür. Yani makine dilinde, yığın üzerinde bir çift bile oluşturulamayabilir. Kod, WriteLineişlev için geçici bir değişken olduğunu yansıttığından, yalnızca bir kayıt olabilir . Sadece döngüler için bir dizi optimizasyon kuralı vardır. Ortalama bir adam bu konuda endişelenmemeli, özellikle de yönetilen dillerde. Örneğin, sadece string a; a+=anotherstring[i]vs kullanarak kullanarak çok sayıda dizeyi birleştirmeniz gerekiyorsa, yönetme kodunu optimize edebileceğiniz durumlar vardırStringBuilder. Her ikisi arasında performansta çok büyük bir fark vardır. Derleyicinin kodunuzu optimize edemediği birçok durum vardır, çünkü daha büyük bir kapsamda neyin amaçlandığını bulamaz. Ancak temel şeyleri sizin için hemen hemen optimize edebilir.


(; j <0x3e8; j ++) için int j = 0 bu şekilde her ikisi de döngü için değil, her iki değişken için bir kez bildirildi. 2) ödev diğer tüm seçenek daha şişman. 3) Yani en iyi uygulama kuralı, yinelemenin dışındaki herhangi bir bildiridir.
luka

24

Bu VB.NET bir gotcha. Visual Basic sonucu, bu örnekte değişkeni yeniden başlatmaz:

For i as Integer = 1 to 100
    Dim j as Integer
    Console.WriteLine(j)
    j = i
Next

' Output: 0 1 2 3 4...

Bu ilk kez 0 (Visual Basic değişkenleri bildirildiğinde varsayılan değerlere sahip!) Ancak ibundan sonra her zaman yazdırır .

= 0Yine de bir eklerseniz , beklediğiniz şeyi elde edersiniz:

For i as Integer = 1 to 100
    Dim j as Integer = 0
    Console.WriteLine(j)
    j = i
Next

'Output: 0 0 0 0 0...

1
VB.NET yıllardır kullanıyorum ve bu rastlamak değildi!
ChrisA

12
Evet, bunu pratikte anlamak tatsız.
Michael Haren

İşte Paul Vick dan bu konuda bir referanstır: panopticoncentral.net/archive/2006/03/28/11552.aspx
ferventcoder

1
@eschneider @ferventcoder Maalesef @PaulV eski blog yayınlarını bırakmaya karar verdi , bu yüzden bu artık ölü bir bağlantı.
Mark Hurd

evet, kısa bir süre önce bunun üzerinde koştu; Bu konuda bazı resmi dokümanlar arıyordu ...
Eric Schneider

15

Basit bir test yaptım:

int b;
for (int i = 0; i < 10; i++) {
    b = i;
}

vs

for (int i = 0; i < 10; i++) {
    int b = i;
}

Bu kodları gcc - 5.2.0 ile derledim. Ve sonra bu iki kodun ana () parçasını söktüm ve sonuç:

1º:

   0x00000000004004b6 <+0>:     push   rbp
   0x00000000004004b7 <+1>:     mov    rbp,rsp
   0x00000000004004ba <+4>:     mov    DWORD PTR [rbp-0x4],0x0
   0x00000000004004c1 <+11>:    jmp    0x4004cd <main+23>
   0x00000000004004c3 <+13>:    mov    eax,DWORD PTR [rbp-0x4]
   0x00000000004004c6 <+16>:    mov    DWORD PTR [rbp-0x8],eax
   0x00000000004004c9 <+19>:    add    DWORD PTR [rbp-0x4],0x1
   0x00000000004004cd <+23>:    cmp    DWORD PTR [rbp-0x4],0x9
   0x00000000004004d1 <+27>:    jle    0x4004c3 <main+13>
   0x00000000004004d3 <+29>:    mov    eax,0x0
   0x00000000004004d8 <+34>:    pop    rbp
   0x00000000004004d9 <+35>:    ret

vs

   0x00000000004004b6 <+0>: push   rbp
   0x00000000004004b7 <+1>: mov    rbp,rsp
   0x00000000004004ba <+4>: mov    DWORD PTR [rbp-0x4],0x0
   0x00000000004004c1 <+11>:    jmp    0x4004cd <main+23>
   0x00000000004004c3 <+13>:    mov    eax,DWORD PTR [rbp-0x4]
   0x00000000004004c6 <+16>:    mov    DWORD PTR [rbp-0x8],eax
   0x00000000004004c9 <+19>:    add    DWORD PTR [rbp-0x4],0x1
   0x00000000004004cd <+23>:    cmp    DWORD PTR [rbp-0x4],0x9
   0x00000000004004d1 <+27>:    jle    0x4004c3 <main+13>
   0x00000000004004d3 <+29>:    mov    eax,0x0
   0x00000000004004d8 <+34>:    pop    rbp
   0x00000000004004d9 <+35>:    ret 

Hangi aynı asm sonuç exaclty vardır. iki kodun aynı şeyi ürettiğinin kanıtı değil mi?


3
evet, ve bunu yapmanız harika, ama bu insanların dil / derleyici bağımlılığı hakkında söylediklerine geri dönüyor. JIT'in veya yorumlanmış dil performansının nasıl etkileneceğini merak ediyorum.
user137717

12

Dile bağlıdır - IIRC C # bunu optimize eder, bu yüzden fark yoktur, ancak JavaScript (örneğin) her seferinde tüm bellek ayırma işlemini gerçekleştirir.


Evet, ama bu fazla bir şey değil. Ben 100 milyon kez yürütmek için bir döngü ile basit bir test yaptım ve döngü dışında ilan lehine en büyük fark 8 ms bulundu. Genellikle 3-4 gibiydi ve bazen döngü dışında bildirmek WORSE (4 ms'ye kadar) yaptı, ancak bu tipik değildi.
user137717

11

Ben her zaman (derleyiciye güvenmek yerine) kullanırsınız ve ayrıca yeniden yazabilirsiniz:

for(int i=0, double intermediateResult=0; i<1000; i++){
    intermediateResult = i;
    System.out.println(intermediateResult);
}

Bu hala intermediateResultdöngünün kapsamıyla sınırlıdır , ancak her yineleme sırasında yeniden tanımlanmaz.


12
Kavramsal olarak değişkenin, yineleme başına ayrı ayrı değil döngü süresi boyunca yaşamasını istiyor musunuz? Nadiren yaparım. Aksi takdirde çok, çok iyi bir sebebiniz olmadığı sürece niyetinizi olabildiğince açık bir şekilde ortaya koyan bir kod yazın.
Jon Skeet

4
Ah, güzel uzlaşma, bunu hiç düşünmemiştim! IMO, kod biraz daha az görsel olarak 'net' hale geliyor)
Rabarberski

2
@Jon - OP'nin ara değerle gerçekten ne yaptığını bilmiyorum. Sadece dikkate değer bir seçenek olduğunu düşündüm.
Triptik

6

Bence b daha iyi bir yapıdır. A öğesinde, döngünüz bittikten sonra ara araın son değeri dolanır.

Düzenleme: Bu, değer türleri ile çok fazla bir fark yaratmaz, ancak referans türleri biraz ağır olabilir. Şahsen, değişkenlerin temizlik için mümkün olan en kısa sürede silinmesini seviyorum ve b bunu sizin için yapar,


sticks around after your loop is finished- bu, Python gibi bir dilde önemli olmasa da, bağlı isimler işlev sona erene kadar dolaşır.
yeni123456

@ new123456: OP soru bile Java özelliklerini istedi edildi biraz jenerik istedi. C-türetilmiş birçok dilde blok düzeyinde kapsam belirleme vardır: C, C ++, Perl ( myanahtar kelimeyle birlikte), C # ve Java 5 ismini kullandım.
Powerlord

Biliyorum - bu bir gözlemdi, eleştiri değil.
yeni123456

5

Birkaç derleyici aynı kod olmak için her ikisi de optimize olabilir, ama kesinlikle hepsi şüpheli. Yani söyleyebilirim ki öncekinden daha iyisin. İkincisinin tek nedeni, bildirilen değişkenin yalnızca döngü içinde kullanılmasını sağlamak istiyorsanız .


5

Genel bir kural olarak değişkenlerimi mümkün olan en içsel kapsamda beyan ederim. Bu yüzden, loopateResult'u döngü dışında kullanmıyorsanız, B ile giderdim.


5

Bir iş arkadaşı ilk formu tercih eder ve ona bir optimizasyon olduğunu söyler ve bir bildirimi yeniden kullanmayı tercih eder.

Ben ikinci bir tercih (ve meslektaşımı ikna etmeye çalışın! ;-)), bunu okuduktan sonra:

  • Değişkenlerin kapsamını ihtiyaç duydukları yere azaltır, bu iyi bir şeydir.
  • Java, performansta önemli bir fark yaratmayacak kadar optimize eder. IIRC, belki de ikinci form daha da hızlıdır.

Her neyse, derleyici ve / veya JVM kalitesine dayanan erken optimizasyon kategorisine girer.


5

Değişkeni bir lambda, vb.'de kullanıyorsanız C # 'da bir fark vardır. Ancak genel olarak, değişkenin sadece döngü içinde kullanıldığı varsayılarak, derleyici temel olarak aynı şeyi yapar.

Temel olarak aynı oldukları göz önüne alındığında: b sürümünün, okuyucular için değişkenin döngüden sonra kullanılamadığını ve kullanılamayacağını çok daha belirgin hale getirdiğini unutmayın. Ek olarak, sürüm b çok daha kolay bir şekilde yeniden düzenlenir. A versiyonunda ilmek gövdesini kendi yöntemine çıkarmak daha zordur. Dahası, sürüm b, böyle bir yeniden düzenlemenin yan etkisi olmadığını garanti eder.

Bu nedenle, sürüm a beni sonuna kadar rahatsız ediyor, çünkü bunun bir faydası yok ve kod hakkında mantık yürütmeyi çok daha zor hale getiriyor ...


5

Bunun için her zaman bir kapsam oluşturabilirsiniz:

{ //Or if(true) if the language doesn't support making scopes like this
    double intermediateResult;
    for (int i=0; i<1000; i++) {
        intermediateResult = i;
        System.out.println(intermediateResult);
    }
}

Bu şekilde değişkeni yalnızca bir kez bildirirsiniz ve döngüden çıktığınızda ölür.


4

Daima değişkenlerinizi döngünüzün içinde bildirirseniz, o zaman hafızayı boşa harcadığınızı düşünmüştüm. Böyle bir şey varsa:

for(;;) {
  Object o = new Object();
}

O zaman sadece her bir yineleme için nesnenin oluşturulması gerekmez, aynı zamanda her bir nesne için yeni bir referans tahsis edilmelidir. Çöp toplayıcı yavaşsa, temizlenmesi gereken bir sürü sarkan referansınız olacaktır.

Ancak, buna sahipseniz:

Object o;
for(;;) {
  o = new Object();
}

Ardından, her seferinde yalnızca tek bir başvuru oluşturup ona yeni bir nesne atarsınız. Elbette, kapsam dışına çıkması biraz daha uzun sürebilir, ancak sonra başa çıkmak için sadece bir sarkan referans var.


3
Referans 'for' döngüsü içinde bildirilse bile, her nesne için yeni bir referans ayrılmaz. İKİ durumlarda: 1) 'o' yerel bir değişkendir ve fonksiyonun başlangıcında yığın alanı bir kez tahsis edilir. 2) Her bir yinelemede yeni bir Nesne oluşturulur. Yani performansta bir fark yok. Kod organizasyonu, okunabilirliği ve sürdürülebilirliği için, referansın döngü içinde bildirilmesi daha iyidir.
Ajoy Bhatia

1
Java için konuşamasam da, .NET'te başvuru ilk örnekte her nesne için 'ayrılmış' değildir. Bu yerel (yönteme) değişkeni için yığın üzerinde tek bir giriş vardır. Örnekleriniz için, oluşturulan IL aynıdır.
Jesse C. Slicer

3

Ben derleyiciye bağlıdır ve genel bir cevap vermek zor olduğunu düşünüyorum.


3

Uygulamam şu şekildedir:

  • değişken tipi basitse (int, double, ...) b varyantını (içeride) tercih ederim .
    Sebep: değişkenin kapsamını azaltmak.

  • değişken türü basit değilse (bir tür classveya struct) a (dış) varyantını tercih ederim .
    Sebep: ctor-dtor çağrılarının sayısını azaltmak.


1

Performans açısından bakıldığında, dışarısı (çok) daha iyidir.

public static void outside() {
    double intermediateResult;
    for(int i=0; i < Integer.MAX_VALUE; i++){
        intermediateResult = i;
    }
}

public static void inside() {
    for(int i=0; i < Integer.MAX_VALUE; i++){
        double intermediateResult = i;
    }
}

Her iki işlevi de her biri 1 milyar kez yerine getirdim. outside () 65 milisaniye sürdü. inside () 1.5 saniye sürdü.


2
O zaman optimize edilmemiş derleme Debug olmalı, ha?
Tomasz Przychodzki

(; j <0x3e8; j ++) için int j = 0 bu şekilde her ikisi de döngü için değil, her iki değişken için bir kez bildirildi. 2) ödev diğer tüm seçenek daha şişman. 3) Yani en iyi uygulama kuralı, yinelemenin dışındaki herhangi bir bildiridir.
luka

1

Ben ilgilenen varsa Node 4.0.0 ile JS için test. Döngünün dışında bildirilmesi, deneme başına 100 milyon döngü yinelemesi ile 1000 den fazla denemede ~ 0.5 ms'lik bir performans artışı ile sonuçlandı. Bu yüzden devam edip B, imo olan en okunabilir / bakımı en iyi şekilde yazacağım diyeceğim. Kodumu bir kemanın içine koyardım, ama şimdi performans düğümü modülünü kullandım. İşte kod:

var now = require("../node_modules/performance-now")

// declare vars inside loop
function varInside(){
    for(var i = 0; i < 100000000; i++){
        var temp = i;
        var temp2 = i + 1;
        var temp3 = i + 2;
    }
}

// declare vars outside loop
function varOutside(){
    var temp;
    var temp2;
    var temp3;
    for(var i = 0; i < 100000000; i++){
        temp = i
        temp2 = i + 1
        temp3 = i + 2
    }
}

// for computing average execution times
var insideAvg = 0;
var outsideAvg = 0;

// run varInside a million times and average execution times
for(var i = 0; i < 1000; i++){
    var start = now()
    varInside()
    var end = now()
    insideAvg = (insideAvg + (end-start)) / 2
}

// run varOutside a million times and average execution times
for(var i = 0; i < 1000; i++){
    var start = now()
    varOutside()
    var end = now()
    outsideAvg = (outsideAvg + (end-start)) / 2
}

console.log('declared inside loop', insideAvg)
console.log('declared outside loop', outsideAvg)

0

A) B'den daha güvenli bir bahis) ......... Yapıyı 'int' ya da 'float' yerine döngü halinde başlatıyorsanız ne yapmalısınız?

sevmek

typedef struct loop_example{

JXTZ hi; // where JXTZ could be another type...say closed source lib 
         // you include in Makefile

}loop_example_struct;

//then....

int j = 0; // declare here or face c99 error if in loop - depends on compiler setting

for ( ;j++; )
{
   loop_example loop_object; // guess the result in memory heap?
}

Kesinlikle bellek sızıntısıyla ilgili sorunlarla karşı karşıyasınız !. Bu nedenle, 'B', yakın kaynak kütüphanelerinde çalışan bellek birikimine karşı savunmasızken 'A'nın daha güvenli bir bahis olduğuna inanıyorum. Linux'ta özellikle' Helgrind 'alt aracı olan' Valgrind 'Aracını kontrol edebilirsiniz.


0

Bu ilginç bir soru. Deneyimlerime göre, bu konuyu bir kod için tartışırken dikkate alınması gereken nihai bir soru var:

Değişkenin küresel olması için herhangi bir neden var mı?

Değişkeni, yerel olarak birçok kez aksine, küresel olarak yalnızca bir kez bildirmek mantıklıdır, çünkü kodu düzenlemek daha iyidir ve daha az kod satırı gerektirir. Ancak, yalnızca bir yöntem içinde yerel olarak bildirilmesi gerekiyorsa, bu yöntemde başlatırım, böylece değişkenin yalnızca bu yöntemle alakalı olduğu açıktır. Bu seçeneği, ikinci seçeneği seçerseniz başlatıldığı yöntemin dışında çağırmamaya dikkat edin; kodunuz ne hakkında konuştuğunuzu bilmez ve bir hata bildirir.

Ayrıca, bir yan not olarak, amaçları neredeyse aynı olsa bile, yerel değişken adlarını farklı yöntemler arasında çoğaltmayın; sadece kafa karıştırıcı oluyor.


1
lol pek çok nedenden dolayı katılmıyorum ... Ancak, aşağı oy yok ... Seçim hakkınıza saygı duyuyorum
Grantly

0

bu daha iyi form

double intermediateResult;
int i = byte.MinValue;

for(; i < 1000; i++)
{
intermediateResult = i;
System.out.println(intermediateResult);
}

1) Bu şekilde, her biri döngü için değil, her iki değişken için de bir kez bildirilir. 2) ödev diğer tüm seçenek daha şişman. 3) Yani en iyi uygulama kuralı, yinelemenin dışındaki herhangi bir bildiridir.


0

Go'da aynı şeyi denedi ve derleyici çıktısını go tool compile -Sgo 1.9.4 ile karşılaştırdı

Montajcı çıkışına göre sıfır fark.


0

Uzun zamandır bu aynı soruyu sordum. Bu yüzden daha basit bir kod parçasını test ettim.

Sonuç: For gibi durumlarda vardır YOK performans farkı.

Dış döngü durumda

int intermediateResult;
for(int i=0; i < 1000; i++){
    intermediateResult = i+2;
    System.out.println(intermediateResult);
}

İç döngü durumda

for(int i=0; i < 1000; i++){
    int intermediateResult = i+2;
    System.out.println(intermediateResult);
}

Derlenmiş dosyayı IntelliJ'in kod çözücüsünde kontrol ettim ve her iki durumda da aynı şeyi aldım Test.class

for(int i = 0; i < 1000; ++i) {
    int intermediateResult = i + 2;
    System.out.println(intermediateResult);
}

Ayrıca, bu yanıtta verilen yöntemi kullanarak her iki durum için de kodu söktüm . Sadece cevapla ilgili kısımları göstereceğim

Dış döngü durumda

Code:
  stack=2, locals=3, args_size=1
     0: iconst_0
     1: istore_2
     2: iload_2
     3: sipush        1000
     6: if_icmpge     26
     9: iload_2
    10: iconst_2
    11: iadd
    12: istore_1
    13: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
    16: iload_1
    17: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
    20: iinc          2, 1
    23: goto          2
    26: return
LocalVariableTable:
        Start  Length  Slot  Name   Signature
           13      13     1 intermediateResult   I
            2      24     2     i   I
            0      27     0  args   [Ljava/lang/String;

İç döngü durumda

Code:
      stack=2, locals=3, args_size=1
         0: iconst_0
         1: istore_1
         2: iload_1
         3: sipush        1000
         6: if_icmpge     26
         9: iload_1
        10: iconst_2
        11: iadd
        12: istore_2
        13: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        16: iload_2
        17: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
        20: iinc          1, 1
        23: goto          2
        26: return
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
           13       7     2 intermediateResult   I
            2      24     1     i   I
            0      27     0  args   [Ljava/lang/String;

Eğer dikkat ederseniz, sadece Slotatanmış ive intermediateResultiçinde LocalVariableTablegeçiş sırasına ürünü olarak takas edilir. Yuvadaki aynı fark diğer kod satırlarına da yansır.

  • Ekstra işlem yapılmıyor
  • intermediateResult her iki durumda da hala yerel bir değişkendir, bu nedenle erişim süresi farkı yoktur.

BONUS

Derleyiciler bir ton optimizasyon yapar, bu durumda ne olduğuna bir göz atın.

Sıfır çalışma çantası

for(int i=0; i < 1000; i++){
    int intermediateResult = i;
    System.out.println(intermediateResult);
}

Sıfır iş ayrıştırıldı

for(int i = 0; i < 1000; ++i) {
    System.out.println(i);
}

-1

Derleyicimin yeterince akıllı olduğunu bilsem bile, ona güvenmek istemeyeceğim ve a) varyantını kullanacağım.

B) varyantı bana sadece döngü gövdesinden sonra ara ara Sonuçun kullanılamaz hale getirilmesi gerekiyorsa bana mantıklı geliyor . Ama yine de böyle umutsuz bir durum hayal edemiyorum ....

EDIT: Jon Skeet çok iyi bir noktaya değindi , bir döngü içindeki değişken bildirimin gerçek anlambilimsel bir fark yaratabileceğini gösterdi.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.