คำถาม
ฉันต้องลบองค์ประกอบที่ไม่ได้ระบุออกจากคอลเลกชันแรกจากคอลเลกชันของ Java ตามเนื้อหาของคอลเลกชันอื่น มันดูง่ายมาก แต่มันเป็นปัญหา
นี่คือหัวของวิธีที่ฉันต้องการเขียน
Void Private Screenblacknamelist (รายการ <sharedboardsmswrapper> แหล่งที่มารายการ <blacknamelistmodel> Blacknamelist)
นี่คือสิ่งที่เป็นอย่างไร คอลเลกชัน ต้นทาง บันทึกองค์ประกอบข้อมูลการแสดงผลบางส่วน คอลเลกชัน Blacknamelist บันทึกรายการบัญชีดำ เราจำเป็นต้องลบข้อมูลของผู้ใช้ที่ขึ้นบัญชีดำในคอลเลก ชันต้นทาง ตามตารางบัญชีดำ
การแก้ปัญหานี้ดูเหมือนง่ายมาก
ก่อนอื่นฉันใช้คำสั่งสำหรับแต่ละคำสั่งเพื่อลบ
สำหรับ (Sharedboardsmswrapper tmpsharedboardsmswrapper: แหล่งที่มา) {สำหรับ (blacknamelistmodel tmpblacknamelistmodel: Blacknamelist) {ถ้า (tmpsharedboardsmswrapper.getSource () source.remove (tmpsharedboardsmswrapper); หยุดพัก; -คำถามง่าย ๆ ! ฉันหัวเราะอย่างลับๆ
ทดสอบ…
สิ่งที่ทำให้ฉันประหลาดใจคือรหัสนี้จริง ๆ
java.util.ConcurrentModificationException。
ดูคู่มือ JDK6
ชั้นเรียนสาธารณะพร้อมกัน modificationExceptionExtends runtimeException
ข้อยกเว้นนี้จะถูกโยนลงเมื่อวิธีการตรวจจับการดัดแปลงพร้อมกันของวัตถุ แต่ไม่อนุญาตให้มีการดัดแปลงดังกล่าว
ตัวอย่างเช่นเมื่อเธรดวนซ้ำใน คอลเลกชัน คอลเลกชัน อื่นไม่ได้รับอนุญาตให้แก้ไขเชิงเส้น โดยปกติในกรณีเหล่านี้ผลลัพธ์ของการทำซ้ำไม่แน่นอน หากตรวจพบพฤติกรรมนี้การใช้งานตัววนซ้ำบางอย่าง (รวมถึงการใช้งาน การรวบรวม ทั่วไปทั้งหมดที่จัดทำโดย JRE) อาจเลือกที่จะโยนข้อยกเว้นนี้ ตัววนซ้ำที่ดำเนินการนี้เรียกว่าตัววนซ้ำอย่างรวดเร็วเนื่องจากตัววนซ้ำล้มเหลวอย่างรวดเร็วโดยไม่เสี่ยงต่อความเสี่ยงของพฤติกรรมที่ไม่แน่นอนโดยพลการในบางครั้งในอนาคต
โปรดทราบว่าข้อยกเว้นนี้ไม่ได้ระบุเสมอว่าวัตถุได้รับการแก้ไขพร้อมกันโดยเธรดอื่น หากเธรดเดี่ยวออกลำดับของวิธีการเรียกที่ละเมิดสัญญาของวัตถุวัตถุอาจส่งข้อยกเว้นนี้ ตัวอย่างเช่นหากเธรดแก้ไข คอลเลกชัน โดยตรงเมื่อวนซ้ำใน คอลเลกชัน โดยใช้ตัววนซ้ำที่ล้มเหลวอย่างรวดเร็วตัววนซ้ำจะโยนข้อยกเว้นนี้
โปรดทราบว่าพฤติกรรมความล้มเหลวอย่างรวดเร็วของตัววนซ้ำไม่สามารถรับประกันได้เพราะโดยทั่วไปแล้วมันเป็นไปไม่ได้ที่จะรับประกันได้อย่างหนักว่ามีการปรับเปลี่ยนที่เกิดขึ้นพร้อมกันหรือไม่ การดำเนินการล้มเหลวอย่างรวดเร็วจะทำให้ดีที่สุดในการโยน ConcurrentModificationException Exception ดังนั้นจึงผิดที่จะเขียนโปรแกรมที่ขึ้นอยู่กับข้อยกเว้นนี้เพื่อปรับปรุงความถูกต้องของการดำเนินการดังกล่าว วิธีที่ถูกต้องคือ : ConcurrentModificationException ควรใช้เพื่อตรวจจับข้อบกพร่องเท่านั้น
For each ใน Java ใช้ ตัววนซ้ำ สำหรับการประมวลผล ตัววนซ้ำ ไม่อนุญาตให้ลบคอลเลกชันระหว่างการใช้ ตัววนซ้ำ และเมื่อฉันอยู่ใน for each ฉันจะลบองค์ประกอบออกจากคอลเลกชันซึ่งทำให้ ตัววน ซ้ำต้องโยน ConcurrentModificationException Exception
ดูเหมือนว่าเราสามารถใช้แบบดั้งเดิมสำหรับการวนซ้ำได้อย่างตรงไปตรงมา!
สำหรับ (int i = 0; i <source.size (); i ++) {Sharedboardsmswrapper tmpsharedboardsmswrapper = source.get (i); สำหรับ (int j = 0; j <blacknamelist.size (); j ++) {blacknamelistmodel tmpblacknamelistmodel = blacknamelist.get (j); if (tmpsharedboardsmswrapper.getSource (). เท่ากับ (tmpblacknamelistmodel.getSource ())) {source.remove (tmpsharedboardsmswrapper); หยุดพัก; }}}ตอนนี้ควรจะดี! กดทดสอบด้วยความมั่นใจ ...
เป็นลม! เกิดอะไรขึ้น? ข้อมูลจะถูกกรองผิดได้อย่างไร?
หลังจากการติดตาม การดีบัก พบว่าเมื่อชุดลบองค์ประกอบขนาดของชุดจะเล็กลงและดัชนีจะเปลี่ยน!
ฉันควรทำอย่างไร? ฉันจะไม่ทำอะไรไม่ถูกโดยปัญหาเล็ก ๆ น้อย ๆ !
ใช้ตัววนซ้ำเพื่อลบองค์ประกอบในการรวบรวม
ตรวจสอบอินเทอร์เฟซตัววนซ้ำของคู่มือ JDK และดูว่ามันยังมีวิธี การลบ
ลบ
โมฆะลบ ()
ลบองค์ประกอบสุดท้ายที่ส่งคืนโดยตัววนซ้ำออกจาก คอลเลคชั่น ที่ชี้ไปที่ตัววนซ้ำ (การดำเนินการเสริม) วิธีนี้สามารถเรียกได้เพียงครั้งเดียวต่อการโทรถัดไป หากตัววนซ้ำได้รับการแก้ไขโดย คอลเลกชัน ที่ชี้ไปที่ตัววนซ้ำโดยใช้วิธีการอื่นนอกเหนือจากการเรียกใช้วิธีนี้พฤติกรรมของตัววนซ้ำจะไม่แน่นอน
โยน:
UnsupportedOperationException - หากตัววนซ้ำไม่รองรับการดำเนินการ ลบ
IllegalStateException - หากไม่ได้เรียกวิธี การต่อไป หรือวิธี การลบ ได้รับการเรียกหลังจากการโทรครั้งสุดท้ายไปยังวิธี ถัดไป
แก้ไขรหัสสุดท้าย:
/ ** *@paramsource *@paramblacknamelist */ privatevoid screenblacknamelist (รายการ <sharedboardsmswrapper>, รายการ <blacknamelistmodel> blacknamelist) {iterator <HaredBoardsMSWRAPPER> แหล่งที่มา = แหล่งที่มา ในขณะที่ (sourceit.hasnext ()) {Sharedboardsmswrapper tmpsharedboardsmswrapper = sourceit.next (); ตัววนซ้ำ <blacknamelistmodel> blacknamelistit = blacknamelist.iterator (); ในขณะที่ (blacknamelistit.hasnext ()) {blacknamelistmodel tmpblacknamelistmodel = blacknamelistit.next (); if (tmpsharedboardsmswrapper.getSource (). เท่ากับ (tmpblacknamelistmodel.getSource ())) {sourceit.remove (); หยุดพัก; - โปรดทราบว่า remove() next() ของ ตัววน ซ้ำไม่สามารถเรียกได้หลายครั้ง มิฉะนั้นจะมีการโยนข้อยกเว้น
ดูเหมือนว่าวิธีที่ง่ายที่สุดในการลบองค์ประกอบในคอลเลกชันคือการใช้วิธี remove() ของ Iterator !
มาดูกันว่า ตัววนซ้ำ จัดทำโดยคลาส ArrayList ได้อย่างไร
Privateclass ITR ใช้ Iterator <e> { /** นี่คือดัชนีขององค์ประกอบซึ่งเทียบเท่ากับตัวชี้หรือเคอร์เซอร์ซึ่งใช้เพื่อเข้าถึงองค์ประกอบข้อมูลของรายการ *INDEXOFElementToBereturnedBysubeMeStCallTonext */ intCursor = 0; /** *indexofelementreturnedbymostrecentCallTonextor *ก่อนหน้า Resetto -1ifthiselementisDeletedByAcall *ToreMove. 最新元素的索引。หากองค์ประกอบถูกลบให้ตั้งค่าเป็น -1 */ intlastret = -1; /** คุณสมบัติของ ArrayList คลาสภายนอก: ป้องกันการชั่วคราว int modCount = 0; มันถูกใช้เพื่อสังเกตว่า ArrayList กำลังถูกแก้ไขโดยเธรดอื่นในเวลาเดียวกันหรือไม่ หากไม่สอดคล้องกันข้อยกเว้นแบบซิงโครนัสจะถูกโยนลงไป *Themodcountvaluethattheiteratorbelievesthatthebacking *listshouldhave หากความคาดหวังนี้ใช้งานนี้ THEITERATOR *HASDETECTENTCRURNEMTMODIFICITION */intexpectedModCount = modCount; // ถ้าเคอร์เซอร์ไม่ถึงขนาดของรายการก็ยังมีองค์ประกอบอยู่ PublicBoolean Hasnext () {returnCursor! = size (); } // ส่งคืนองค์ประกอบปัจจุบันจากนั้นเคอร์เซอร์ +1 ดัชนีล่าสุด = ดัชนีขององค์ประกอบที่ส่งคืน สาธารณะ e next () {checkforcomodification (); ลอง {e next = get (เคอร์เซอร์); Lastret = เคอร์เซอร์ ++; กลับมาถัดไป; } catch (indexoutofboundsexception e) {checkforcomodification (); thrownew nosuchelementexception (); }}/*ลบองค์ประกอบซึ่งหมายถึงการลบองค์ประกอบปัจจุบันและวางเคอร์เซอร์ -1 เพราะรายการจะย้ายองค์ประกอบทั้งหมดต่อไปนี้ไปยังองค์ประกอบก่อนหน้า */ publicvoid ลบ () {ถ้า (lastret == -1) โยน unleegalstateException (); checkforcomodification (); ลอง {Abstractlist.his.remove (Lastret); ถ้าเคอร์เซอร์ (Lastret <เคอร์เซอร์)-; Lastret = -1; คาดว่า ModCount = ModCount; } catch (indexoutofboundsexception e) {thrownew concurrentModificationException (); }} FinalVoid CheckForcomodification () {ถ้า (modcount! = คาดหวัง modcount) thrownew concurrentModificationException (); -สรุป
อย่างที่คุณเห็น Iterator จะลบองค์ประกอบและรีเซ็ตเคอร์เซอร์เป็นที่นั่งที่ถูกต้อง ตราบใดที่ไม่มีเธรดอื่นเปลี่ยนชุดในเวลาเดียวกันก็จะไม่มีปัญหา ข้างต้นเป็นเรื่องเกี่ยวกับบทความนี้ฉันหวังว่ามันจะเป็นประโยชน์สำหรับทุกคนในการเรียนรู้ Java