JavaRush /Java blogi /Random-UZ /Ikki iterator haqida hikoya: Java-da raqobatbardosh modif...

Ikki iterator haqida hikoya: Java-da raqobatbardosh modifikatsiya strategiyalari

Guruhda nashr etilgan
Eslatma muallifi Krakovdan (Polsha) dasturiy ta'minot ishlab chiqaruvchisi Grzegorz Mirek. U Javada taxminan 6 yil oldin, universitetda o'qib yurgan vaqtlarida rivojlana boshlagan va o'sha paytdan beri bu sohada o'z mahoratini tinimsiz sayqallab kelmoqda. U, ayniqsa, JVM ishlashi va optimallashtirishga qiziqadi, u asosan o'z blogida nima haqida yozadi .
Ikki iterator haqida hikoya: Java-da raqobatbardosh modifikatsiya strategiyalari - 1
Java intervyusining eng mashhur savollaridan ba'zilari quyidagilardan iborat: Fast-fast va muvaffaqiyatsiz iteratorlar o'rtasidagi farq nima? Bunga eng soddalashtirilgan javob: Ishlamay qolgan iterator, agar to'plam iteratsiya paytida o'zgarsa, ConcurrentModificationException ni chiqaradi, lekin muvaffaqiyatsiz iterator o'zgarmasa. Garchi bu juda mazmunli bo'lib tuyulsa-da, suhbatdoshning xatoga yo'l qo'ymaslik deganda nimani nazarda tutayotgani noma'lumligicha qolmoqda? Java tili spetsifikatsiyalari bu atamani iteratorlarga nisbatan aniqlamaydi. Biroq, to'rtta raqobatbardosh o'zgartirish strategiyasi mavjud.

Raqobatli modifikatsiya

Birinchidan, raqobatbardosh (yoki parallel) modifikatsiya nima ekanligini aniqlaylik. Aytaylik, bizda to'plam bor va iterator faol bo'lganda, bu iteratordan kelmaydigan ba'zi o'zgarishlar yuz beradi. Bunday holda, biz raqobatbardosh modifikatsiyani olamiz. Sizga oddiy misol keltiraman: deylik, bizda bir nechta mavzu bor. Birinchi ip takrorlanadi, ikkinchi ip esa bir xil to'plamga elementlarni kiritadi yoki olib tashlaydi. Biroq, biz bitta oqimli muhitda ishlaganda ConcurrentModificationException ni olishimiz mumkin:
List<String> cities = new ArrayList<>();
cities.add(Warsaw);
cities.add(Prague);
cities.add(Budapest);

Iterator<String> cityIterator = cities.iterator();
cityIterator.next();
cities.remove(1);
cityIterator.next(); // генерирует ConcurrentModificationException

Muvaffaqiyatsiz

Yuqoridagi kod fragmenti muvaffaqiyatsiz tez iteratorga misoldir . Ko'rib turganingizdek, iteratordan ikkinchi elementni olishga urinayotganda ConcurrentModificationException yuborildi . Iterator to'plam yaratilganidan beri o'zgartirilganligini qanday biladi? Misol uchun, to'plamda sana/vaqt tamg'asi bo'lishi mumkin, aytaylik lastModified . Iteratorni yaratishda siz ushbu maydonni nusxalashingiz va uni iterator ob'ektida saqlashingiz kerak. Keyin, har safar keyingi() usuli chaqirilganda, siz kollektsiyadagi lastModified qiymatini iterator nusxasi bilan solishtirasiz . Juda o'xshash yondashuv, masalan, ArrayList sinfini amalga oshirishda qo'llaniladi . Unda modCount misol oʻzgaruvchisi mavjud boʻlib , u roʻyxat necha marta oʻzgartirilganligini saqlaydi:
final void checkForComodification() {
   if (modCount != expectedModCount)
       throw new ConcurrentModificationException();
}
Shuni ta'kidlash kerakki, muvaffaqiyatsiz tez iteratorlar eng zo'r asosda ishlaydi, ya'ni bir vaqtning o'zida o'zgartirilganda ConcurrentModificationException bekor qilinishiga kafolat yo'q. Shuning uchun siz ularga ishonmasligingiz kerak - aksincha, ular xatolarni aniqlash uchun ishlatilishi kerak. Bir vaqtning o'zida bo'lmagan to'plamlarning aksariyati muvaffaqiyatsiz tez iteratorlarni ta'minlaydi.

Zaif izchillik

Java.util.concurrent paketidagi (masalan , ConcurrentHashMap va most Queue kabi ) ko'pchilik bir vaqtda to'plamlar zaif izchil iteratorlarni ta'minlaydi. Ushbu atamaning ma'nosi hujjatlarda juda yaxshi tushuntirilgan :
  • Ular boshqa operatsiyalar bilan bir vaqtda qayta ishlanishi mumkin
  • Ular hech qachon ConcurrentModificationException ni tashlamaydilar
  • Ular iterator aynan bir marta yaratilgan vaqtda mavjud elementlarni kesib o'tishlari kafolatlanadi va keyingi o'zgartirishlarni aks ettirishi mumkin (lekin talab qilinmaydi).

Surat

Ushbu strategiya yordamida iterator to'plamning yaratilish vaqtidagi holati bilan bog'liq - bu to'plamning oniy tasviri. Asl to'plamga kiritilgan har qanday o'zgarishlar asosiy ma'lumotlar strukturasining yangi versiyasini yaratishga olib keladi. Bu bizning oniy rasmimizni o'zgarishsiz qoldiradi, shuning uchun u iterator yaratilgandan keyin to'plamga kiritilgan o'zgarishlarni aks ettirmaydi. Bu eski yaxshi nusxa ko'chirish va yozish (COW) texnikasi . U bir vaqtning o'zida o'zgartirishlar muammosini to'liq hal qiladi, shuning uchun bu yondashuv bilan ConcurrentModificationException yaratilmaydi. Bundan tashqari, iteratorlar elementlarni o'zgartiradigan operatsiyalarni qo'llab-quvvatlamaydi. Yozuvga nusxa ko'chirish to'plamlaridan foydalanish juda qimmatga tushadi, ammo agar o'zgarishlar iterator o'tishlariga qaraganda kamroq sodir bo'lsa, ulardan foydalanish mantiqan to'g'ri keladi. Masalan, CopyOnWriteArrayList va CopyOnWriteArraySet sinflari .

Belgilanmagan xatti-harakatlar

Vektor va Hashtable kabi eski to'plam turlarida aniqlanmagan xatti-harakatlarga duch kelishingiz mumkin . Ikkalasida ham standart muvaffaqiyatsiz iteratorlar mavjud, ammo qo'shimcha ravishda ular Enumeration interfeysining ilovalaridan foydalanishga imkon beradi va ular bir vaqtda o'zgartirilganda o'zini qanday tutishni bilishmaydi. Siz ba'zi elementlarning takrorlanishi yoki yo'qolishi yoki hatto ba'zi g'alati istisnolarga duch kelishingiz mumkin. Ular bilan o'ynamaslik yaxshiroqdir!
Izohlar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION