سؤال
أحتاج إلى إزالة عناصر غير محددة من المجموعة الأولى من مجموعة من Java ، استنادًا إلى محتوى مجموعة أخرى. هذا يبدو بسيطًا جدًا ، لكنها مشكلة.
هذا هو رئيس الطريقة التي أريد أن أكتبها
Private void screenblacknamelist (قائمة <SharboardsMswrapper> المصدر ، القائمة <LacknamelistModel> Blacknamelist)
هذه هي الطريقة التي تكون بها الأمور. تقوم مجموعة المصدر بحفظ بعض عناصر بيانات العرض. مجموعة Blacknamelist يحفظ قائمة القائمة السوداء. نحتاج إلى إزالة بيانات المستخدمين المدرجين في القائمة السوداء في مجموعة المصدر استنادًا إلى جدول القائمة السوداء.
يبدو أن الحل لهذه المشكلة بسيط للغاية.
أنا أولا استخدام لكل عبارة لحذف.
لـ (sharedboardsmswrapper tmpsharedboardoardsmswrapper: source) {for (blackamelistmodel tmpblackennamelistmodel: blacknamelist) {if (tmpsharedboardsmswrapper.getsource (). source.remove (tmpsharedboardsmswrapper) ؛ استراحة؛ }}}سؤال بسيط جدا! ضحكت سرا ،
امتحان…
ما فاجأني هو أن هذا الرمز قد ألقى استثناء بالفعل
java.util.ConcurrentModificationException。
عرض دليل JDK6
الطبقة العامة ConversIdificationExceptionExtends RunDimeException
يتم طرح هذا الاستثناء عندما تكتشف الطريقة تعديلًا متزامنًا للكائن ولكنه لا يسمح بهذا التعديل.
على سبيل المثال ، عندما يتكرر مؤشر ترابط على مجموعة ، لا يُسمح بتعديل مجموعة أخرى خطيًا. عادة في هذه الحالات ، تكون نتائج التكرار غير مؤكدة. إذا تم اكتشاف هذا السلوك ، فقد تختار بعض تطبيقات التكرار (بما في ذلك جميع تطبيقات التجميع المشتركة التي توفرها JRE) إلقاء هذا الاستثناء. يُطلق على التكرار الذي يؤدي هذه العملية تكرار الفشل السريع لأن التكرار يفشل بسرعة كبيرة دون المخاطرة بخطر السلوك غير المؤكد التعسفي في وقت ما في المستقبل.
لاحظ أن هذا الاستثناء لا يشير دائمًا إلى أنه تم تعديل الكائن بشكل متزامن بواسطة مؤشر ترابط مختلف. إذا أصدر مؤشر ترابط واحد سلسلة من مكالمات الطريقة التي تنتهك عقد الكائن ، فقد يرمي الكائن هذا الاستثناء. على سبيل المثال ، إذا قام مؤشر ترابط بتعديل المجموعة مباشرةً عندما يتكرر على المجموعة باستخدام ITERATOR FAST FAIL ، فسيقوم Iterator بإلقاء هذا الاستثناء.
لاحظ أنه لا يمكن ضمان سلوك الفشل السريع للمؤلف ، لأنه بشكل عام ، من المستحيل تقديم أي ضمانات صعبة حول ما إذا كان هناك تعديل متزامن خارجي. ستقوم عملية فشل سريعة ببذل قصارى جهدها لرمي ConcurrentModificationException . لذلك ، من الخطأ كتابة برنامج يعتمد على هذا الاستثناء لتحسين صحة هذه العمليات. الطريقة الصحيحة هي : يجب استخدام ConcurrentModificationException فقط للكشف عن الأخطاء.
For each في Java يستخدم بالفعل Iterator للمعالجة. لا يسمح ITERATOR بحذف المجموعات أثناء استخدام التكرار . وعندما كنت في for each ، قمت بحذف عنصر من المجموعة ، مما تسبب في إلقاء التكرار ConcurrentModificationException .
يبدو أنه يمكننا فقط استخدام التقليدية للحلقة بصراحة!
لـ (int i = 0 ؛ i <source.size () ؛ i ++) {sharboroardsmswrapper tmpsharedboardoardsmswrapper = source.get (i) ؛ لـ (int j = 0 ؛ j <Blacknamelist.size () ؛ j ++) {BlacknamelistModel tmpBlackElistModel = Blacknamelist.get (j) ؛ if (tmpsharedboardsmswrapper.getsource (). يساوي (tmpblacknamelistmodel.getsource ())) {source.remove (tmpsharedbooardsmswrapper) ؛ استراحة؛ }}}يجب أن يكون بخير الآن! اضغط على الاختبار بثقة ...
إِغماء! ماذا يحدث هنا؟ كيف يمكن تصفية البيانات خاطئة؟
بعد تتبع التصحيح ، وجد أنه عندما يحذف المجموعة عناصر ، سيصبح حجم المجموعة أصغر وسيتغير الفهرس!
ماذا علي أن أفعل؟ لن أكون عاجزًا بمثل هذه المشكلة الصغيرة!
استخدم Iterator لحذف العناصر في التجميع
تحقق من واجهة التكرار لدليل JDK وانظر إلى أنه يحتوي أيضًا على طريقة إزالة .
يزيل
remove () باطل ()
قم بإزالة العنصر الأخير الذي تم إرجاعه بواسطة Iterator من المجموعة التي أشار إليها Iterator (العملية الاختيارية). لا يمكن استدعاء هذه الطريقة إلا مرة واحدة لكل مكالمة بعد ذلك. إذا تم تعديل التكرار من خلال مجموعة تشير إليها من قِبل المتكرر باستخدام طريقة أخرى غير استدعاء هذه الطريقة ، فإن سلوك التكرار غير مؤكد.
يرمي:
UnsupportedOperationException - إذا لم يدعم التكرار عملية إزالة التشغيل.
IllegalStateException - إذا لم يتم استدعاء الطريقة التالية ، أو تم استدعاء طريقة إزالة بعد آخر مكالمة إلى الطريقة التالية .
الكود النهائي الصحيح:
/ ** *@paramsource *@paramblacknamelist */ privatevoid screenblacknamelist (قائمة <SharboBoardSmswRapper> المصدر ، القائمة <ClognamelistModel> Blacknamelist) {iterator <SharboardSmswrapper> sourceit = source.iterator () ؛ بينما (sourceIt.hasNext ()) {sharboroardsmswrapper tmpsharedboardoardsmswrapper = sourceit.next () ؛ iterator <LacknamelistModel> Blacknamelistit = Blacknamelist.iterator () ؛ بينما (blacknamelistit.hasnext ()) {BlacknamelistModel tmpblackennamelistmodel = Blacknamelistit.next () ؛ if (tmpsharedboardsmswrapper.getSource (). متساوٍ (tmpblacknamelistmodel.getsource ())) {sourceit.remove () ؛ استراحة؛ }}}}} لاحظ أنه لا يمكن استدعاء remove() التكرار next() عدة مرات. خلاف ذلك ، سيتم طرح استثناء.
يبدو أن أسهل طريقة لحذف العناصر في المجموعة هي استخدام طريقة remove() ITerator !
دعونا نرى كيف يتم تنفيذ التكرار المقدم من فئة ArrayList .
يقوم PrivateClass ITR بتنفيذ ITerator <e> { /** هذا هو فهرس العنصر ، وهو ما يعادل مؤشر ، أو المؤشر ، والذي يستخدمه للوصول إلى عنصر بيانات القائمة. *indexofelementToBerTurnedBysUbequeNtCalltonext. */ intcursor = 0 ؛ /** *indexofelementRuterNedByMoStreCentCalltonextor *السابق. resetto -1ifthiselementiletedbyacall *toremove. 最新元素的索引。 إذا تم حذف العنصر ، تم ضبطه على -1 */ intlastret = -1 ؛ /** خصائص arraylist الفئة الخارجية: محمية عابرة int modcount = 0 ؛ يتم استخدامه لمراقبة ما إذا كان يتم تعديل قائمة ArrayList بواسطة مؤشرات ترابط أخرى في نفس الوقت. إذا كان غير متناسق ، فسيتم طرح استثناء متزامن. *themodcountvaluethattheIteratorBelieVesthattheBacking *listShouldHave. إذا كان هذا التوقعات ، فإن theiterator *hasdetectedcurentModification. */intexedModCount = modCount ؛ // إذا لم يصل المؤشر إلى حجم القائمة ، فلا يزال هناك عناصر. publicBoolean hasnext () {returncursor! = size () ؛ } // إرجاع العنصر الحالي ثم المؤشر +1. الفهرس الحديث = فهرس العناصر التي تم إرجاعها. public e next () {checkForComodification () ؛ حاول {e next = get (المؤشر) ؛ LASTRET = CURSOR ++ ؛ العودة بعد ذلك ؛ } catch (indexoutofBoundSexception e) {checkForComodification () ؛ rewrownew nosuchelementException () ؛ }}/*حذف عنصر ، مما يعني حذف العنصر الحالي ووضع المؤشر -1. لأن القائمة ستنقل جميع العناصر التالية إلى العناصر السابقة. */ publicvoid remove () {if (lastret == -1) remrownew alficalstateException () ؛ checkForComodification () ؛ Try {AbstractList.This.Remove (Lastret) ؛ إذا (lastret <cursor) Cursor-- ؛ lastret = -1 ؛ المتوقع expectModCount = modCount ؛ } catch (indexoutofBoundSexception e) {turnew convalentModificationException () ؛ }} FinalVoid checkforcomodification () {if (modCount! = expectedModCount) ألقوا convalentModificationexception () ؛ }}لخص
كما ترون ، يحذف ITERATOR العنصر ويعيد تعيين المؤشر إلى المقعد الصحيح. طالما لم تغير خيوط أخرى المجموعة في نفس الوقت ، فلن تكون هناك مشكلة. ما سبق هو كل شيء عن هذا المقال ، آمل أن يكون من المفيد للجميع تعلم جافا.