java - لماذا يتسبب List.addAll بقائمة فرعية معكوسة في القائمة في حدوث ConcurrentModificationException




arraylist collections (3)

تقوم List.subList بإرجاع طريقة عرض حية للقائمة بين العناصر المحددة ، وليس نسخة من تلك العناصر (انظر documentation ) ، وبالتالي فإن إضافة إلى القائمة الأصلية ستقوم أيضًا بتعديل القائمة الفرعية ، والتي ستؤدي إلى ConcurrentModificationException (بما أن ما يتم إضافته و إلى ما تضيفه يتم تعديله أيضًا في نفس الوقت).

list.subList(startIndex, endIndex+1)

يمكنك إصلاح رمز عن طريق نسخ القائمة ، مثل

List<Integer> toReverse = new ArrayList<>(list.subList(startIndex, endIndex+1));

لقد كنت أحاول أخذ قائمة فرعية بقائمة ما ، وعكسها ، ووضع القائمة reversed في موضع البداية. على سبيل المثال ، لنفترض أن لدينا قائمة [1, 2, 3, 4, 5, 6] ، ثم إعطاء مؤشر من مؤشر 2 إلى مؤشر 4 يعطي [1, 2, 5, 4, 3, 6] .

لقد كتبت بعض التعليمات البرمجية لهذا ، ومع ذلك فإنه يعطي ConcurrentModificationException كل مرة (ما لم startIndex == endIndex). ويرد أدناه مثال على استنساخ الحد الأدنى:

int startIndex = 2;
int endIndex = 4;
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
list.add(6);

List<Integer> toReverse = list.subList(startIndex, endIndex+1);
Collections.reverse(toReverse);
list.removeAll(toReverse);
list.addAll(startIndex, toReverse);

استثناء في java.util.ConcurrentModificationException "الرئيسي"
في java.util.ArrayList $ SubList.checkForComodification (مصدر غير معروف)
في java.util.ArrayList $ SubList.size (مصدر غير معروف) في
java.util.AbstractCollection.toArray (مصدر غير معروف) في
java.util.ArrayList.addAll (مصدر غير معروف) في
test.ConcurrentExample.main (ConcurrentExample.java:64)

الخط الفعلي الذي يشير إليه الخطأ هو list.addAll(startIndex, toReverse); .

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


تكمن المشكلة هنا في ArrayList#checkForComodification

private void checkForComodification() {
    if (ArrayList.this.modCount != this.modCount)
        throw new ConcurrentModificationException();
    }
}

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

list.removeAll(...);
list.addAll(...);

واترك هذا الرمز فقط:

List<Integer> toReverse = list.subList(startIndex, endIndex+1);
Collections.reverse(toReverse);

من وثائق ArrayList.subList :

يتم دعم القائمة التي تم إرجاعها بواسطة هذه القائمة ، لذلك تنعكس التغييرات غير الهيكلية في القائمة التي تم إرجاعها في هذه القائمة ، والعكس بالعكس

لذلك عند محاولة إضافة عناصر في فهرس "عرض" القائمة الفرعية ، فإنه ينشئ تعديلًا متزامنًا.







concurrentmodification