الفرق بين تحريك التكرار للأمام باستخدام عبارة for وعبارة while

StackOverflow https://stackoverflow.com/questions/515562

سؤال

عندما أستخدم Iterator of Object أستخدم ملف حائط اللوب (كما هو مكتوب في كل كتاب تعلم جافا، كما التفكير في جافا بروس إيكل):

Iterator it=...

while(it.hasNext()){
    //...
}

ولكن في وقت ما رأيت بدلا من ذلك شخص يستخدم لحلقة:

Iterator it=...
for (Iterator it=...; it.hasNext();){
    //...
}

لا أفهم هذا الاختيار:

  • انا استعمل ال لحلقة عندما يكون لدي مجموعة ذات تسلسل ترتيبي (كمصفوفة) أو بقاعدة خاصة للخطوة (يتم الإعلان عنها بشكل عام كزيادة بسيطة counter++).
  • انا استعمل ال حائط اللوب عندما تنتهي الحلقة ليس لدي هذه القيود ولكن فقط شرط منطقي للخروج.

إنها مسألة ترميز النمط دون سبب آخر أم أن هناك منطقًا آخر (الأداء، على سبيل المثال) لا أعرفه؟

شكرا على كل ردود الفعل

هل كانت مفيدة؟

المحلول

بناء الجملة الصحيح للحلقة هو:

for (Iterator it = ...; it.hasNext(); ){
    //...
}

(الإعلان السابق في التعليمات البرمجية الخاصة بك غير ضروري، بالإضافة إلى الفاصلة المنقوطة الإضافية في عنوان الحلقة.)

سواء كنت تستخدم بناء الجملة هذا أو while الحلقة هي مسألة ذوق، كلاهما يترجمان إلى نفس الشيء تمامًا.بناء الجملة العام للحلقة هو:

for (<init stmt>; <loop cond>; <iterate stmt>) { <body>; }

وهو ما يعادل:

<init stmt>;
while (<loop cond>) { <body>; <iterate stmt>; }

يحرر: في الواقع، الشكلين أعلاه ليسا متساويين تمامًا، لو (كما في السؤال) يتم الإعلان عن المتغير باستخدام عبارة init.في هذه الحالة، سيكون هناك اختلاف في نطاق متغير التكرار.مع حلقة for، يقتصر النطاق على الحلقة نفسها، في حالة حلقة while، ومع ذلك، يمتد النطاق إلى نهاية الكتلة المرفقة (ليست مفاجأة كبيرة، لأن الإعلان خارج الحلقة).

أيضًا، كما أشار آخرون، في الإصدارات الأحدث من Java، هناك تدوين مختصر للحلقة for:

for (Iterator<Foo> it = myIterable.iterator(); it.hasNext(); ) {
    Foo foo = it.next();
    //...
}

يمكن كتابتها على النحو التالي:

for (Foo foo : myIterable) {
    //...
}

باستخدام هذا النموذج، ستفقد بالطبع الإشارة المباشرة إلى المُكرِّر، وهو أمر ضروري، على سبيل المثال، إذا كنت تريد حذف عناصر من المجموعة أثناء التكرار.

نصائح أخرى

إنه مجرد شيء يتعلق بالأسلوب وأنا مثلك أفضل حلقة for عند استخدام شيء ما مع فهرس.إذا كنت تستخدم Java 5، فيجب عليك بالطبع استخدام حلقة foreach فوق المجموعة على أي حال:

Collection<String> someCollection = someCollectionOfString();
for (String element : someCollection) {
....
}

الغرض من إعلان Iterator داخل الحلقة هو تقليل نطاق المتغيرات الخاصة بك, ، وهي ممارسة جيدة.

عندما تعلن Iterator خارج الحلقة، فإن المرجع لا يزال صالحًا/حيًا بعد اكتمال الحلقة.في 99.99% من الحالات، لا تحتاج إلى الاستمرار في استخدام Iterator بمجرد اكتمال الحلقة، لذلك يمكن أن يؤدي هذا النمط إلى أخطاء مثل هذا:

//iterate over first collection
Iterator it1 = collection1.iterator();
while(it1.hasNext()) {
   //blah blah
}

//iterate over second collection
Iterator it2 = collection2.iterator();
while(it1.hasNext()) {
   //oops copy and paste error! it1 has no more elements at this point
}

لا يوجد فرق كبير بين هاتين الطريقتين، بخلاف الطريقة الأولى التي تتذكر قيمة it بعد الحلقة.من المحتمل أن يؤدي الأسلوب الثاني إلى حدوث خطأ، لأنك تعيد تعريف المتغير داخل الحلقة.

هناك في الواقع طريقة ثالثة لهذا أيضًا.بدلا من الكتابة على سبيل المثال.

for (Iterator<SomeObject> it = foo.iterator(); it.hasNext();) {
  SomeObject so = foo.next();
}

يمكنك الكتابة في أغلب الأحيان

for (SomeObject so: foo) {...}

وهذان متساويان لنفس الشيء..

قد يستخدم الأشخاص حلقة for الصريحة ("النمط القديم") ببساطة لأنها تبدو أكثر نظافة بالنسبة لهم، وربما لم يتكيفوا مع بناء الجملة الجديد بعد (والذي أعتقد شخصيًا أنه أكثر نظافة).

إحدى المزايا الفعلية المحددة لبناء حلقة for الأطول هي أن لديك مرجعًا للمكرر وبالتالي يمكنك استدعاء الأساليب عليه بخلاف ذلك next().بخاصة، hasNext() غالبًا ما يكون من المفيد الاتصال به - تخيل أنك تريد طباعة قائمة من السلاسل مفصولة بفواصل:

StringBuilder sb = new StringBuilder();
for (Iterator it=...; it.hasNext();){
   sb.append(it.next());
   if (it.hasNext())
      sb.append(", ");
}

ما عليك سوى التمييز بين حالة "هذا هو الأخير" مثل هذه إذا كنت تستخدم الحلقة الأكثر تفصيلاً.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top