java - لغة - استدعاء إزالة في حلقة foreach في جاوة




شرح المصفوفات في جافا (8)

هذا السؤال لديه بالفعل إجابة هنا:

في Java ، هل من القانوني استدعاء إزالة على مجموعة عند التكرار خلال المجموعة باستخدام حلقة foreach؟ على سبيل المثال:

List<String> names = ....
for (String name : names) {
   // Do something
   names.remove(name).
}

كإضافة ، هل من القانوني إزالة عناصر لم يتم تكرارها بعد؟ على سبيل المثال،

//Assume that the names list as duplicate entries
List<String> names = ....
for (String name : names) {
    // Do something
    while (names.remove(name));
}

  1. جرب هذا 2. وقم بتغيير شرط "WINTER" وستتساءل:
public static void main(String[] args) {
  Season.add("Frühling");
  Season.add("Sommer");
  Season.add("Herbst");
  Season.add("WINTER");
  for (String s : Season) {
   if(!s.equals("Sommer")) {
    System.out.println(s);
    continue;
   }
   Season.remove("Frühling");
  }
 }

أنت لا تريد القيام بذلك. يمكن أن يسبب سلوكًا غير محدد بناءً على المجموعة. تريد استخدام Iterator مباشرة. على الرغم من أن كل بناء هو عبارة عن سكر نحوي ويستخدم حافزاً فعلاً ، فإنه يخفيه من الكود الخاص بك بحيث لا يمكنك الوصول إليه للاتصال بـ Iterator.remove .

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

بدلا من كتابة التعليمات البرمجية الخاصة بك:

List<String> names = ....
Iterator<String> it = names.iterator();
while (it.hasNext()) {

    String name = it.next();
    // Do something
    it.remove();
}

لاحظ أن رمز المكالمات Iterator.remove لا List.remove .

إضافة:

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


استعمال

.remove () من Interator أو

استعمال

CopyOnWriteArrayList


تأكد من أن هذه ليست رائحة الكود. هل من الممكن عكس هذا المنطق وجعله "شاملًا" بدلاً من "حصري"؟

List<String> names = ....
List<String> reducedNames = ....
for (String name : names) {
   // Do something
   if (conditionToIncludeMet)
       reducedNames.add(name);
}
return reducedNames;

تضمنت الحالة التي قادتني إلى هذه الصفحة رمزًا قديمًا يحلق عبر قائمة باستخدام حالات التخلّص لإزالة عناصر من القائمة. كنت أرغب في ريماكتور لاستخدام أسلوب foreach.

تحللت من خلال قائمة كاملة من العناصر للتحقق من تلك التي لدى المستخدم إذن للوصول إليها ، وإزالة تلك التي لم يكن لديها إذن من القائمة.

List<Service> services = ...
for (int i=0; i<services.size(); i++) {
    if (!isServicePermitted(user, services.get(i)))
         services.remove(i);
}

لعكس هذا وعدم استخدام الإزالة:

List<Service> services = ...
List<Service> permittedServices = ...
for (Service service:services) {
    if (isServicePermitted(user, service))
         permittedServices.add(service);
}
return permittedServices;

متى يفضل "إزالة"؟ أحد الاعتبارات هو ما إذا كان gien قائمة كبيرة أو "إضافة" مكلفة ، جنبا إلى جنب مع عدد قليل فقط إزالتها مقارنة مع حجم القائمة. قد يكون من الأكثر فاعلية القيام ببعض عمليات الإزالة فقط بدلاً من إضافة عدد كبير. ولكن في حالتي لم يكن الوضع يستحق هذا التحسين.


لإزالة بأمان من مجموعة أثناء التكرار أكثر من ذلك يجب عليك استخدام Iterator.

فمثلا:

List<String> names = ....
Iterator<String> i = names.iterator();
while (i.hasNext()) {
   String s = i.next(); // must be called before you can call i.remove()
   // Do something
   i.remove();
}

من وثائق جافا :

إن التكرارات التي يتم إرجاعها بواسطة أداة التكرار وأساليب listTator الخاصة بالفصل الدراسي تكون سريعة الفشل: إذا تم تعديل القائمة بطريقة هيكلية في أي وقت بعد إنشاء المكرر ، وبطريقة ما عدا من خلال إزالة أو إضافة أساليب ، فإن المكرر سوف يرمي ConcurrentModificationException. وبالتالي ، في مواجهة التعديل المتزامن ، يفشل المكرر بسرعة ونظيف ، بدلاً من المخاطرة بالسلوك التعسفي غير الحتممي في وقت غير محدد في المستقبل.

ربما ما هو غير واضح للعديد من المبتدئين هو حقيقة أن التكرار على قائمة باستخدام البنى for / foreach يتضمن ضمنيًا مكررًا لا يمكن الوصول إليه بالضرورة. يمكن العثور على هذه المعلومات here


لم أكن على علم بالمكرّرات ، ولكن إليك ما كنت أفعله حتى اليوم لإزالة عناصر من قائمة داخل حلقة:

List<String> names = .... 
for (i=names.size()-1;i>=0;i--) {    
    // Do something    
    names.remove(i);
} 

يعمل هذا دائمًا ، ويمكن استخدامه بلغات أخرى أو بنى غير معتمدة للمكررات.


نعم ، يمكنك استخدام حلقة for-each ، للقيام بذلك ، يجب عليك الاحتفاظ بقائمة منفصلة للاحتفاظ بعناصر إزالة ، ثم إزالة تلك القائمة من قائمة الأسماء باستخدام أسلوب removeAll() ،

List<String> names = ....

// introduce a separate list to hold removing items
List<String> toRemove= new ArrayList<String>();

for (String name : names) {
   // Do something: perform conditional checks
   toRemove.add(name);
}    
names.removeAll(toRemove);

// now names list holds expected values

for (String name : new ArrayList<String>(names)) {
    // Do something
    names.remove(nameToRemove);
}

يمكنك استنساخ names القوائم وتكرارها من خلال النسخ أثناء إزالتك من القائمة الأصلية. نظافة قليلا من أعلى إجابة.





foreach