วนซ้ำผ่านคอลเลกชันหลีกเลี่ยง ConcurrentModificationException เมื่อลบวัตถุในลูป


1194

เราทุกคนรู้ว่าคุณไม่สามารถทำสิ่งต่อไปนี้ได้เนื่องจากConcurrentModificationException:

for (Object i : l) {
    if (condition(i)) {
        l.remove(i);
    }
}

แต่บางครั้งก็ใช้งานได้ แต่ก็ไม่เสมอไป นี่คือรหัสเฉพาะบางส่วน:

public static void main(String[] args) {
    Collection<Integer> l = new ArrayList<>();

    for (int i = 0; i < 10; ++i) {
        l.add(4);
        l.add(5);
        l.add(6);
    }

    for (int i : l) {
        if (i == 5) {
            l.remove(i);
        }
    }

    System.out.println(l);
}

แน่นอนผลลัพธ์นี้ใน:

Exception in thread "main" java.util.ConcurrentModificationException

แม้ว่าจะมีหลายเธรดก็ตาม อย่างไรก็ตาม.

ทางออกที่ดีที่สุดสำหรับปัญหานี้คืออะไร? ฉันจะลบไอเท็มออกจากคอลเล็กชันในลูปโดยไม่ละทิ้งข้อยกเว้นนี้ได้อย่างไร?

ฉันยังใช้กฎเกณฑ์Collectionที่นี่ไม่จำเป็นต้องเป็นArrayListดังนั้นคุณจึงไม่สามารถไว้ใจgetได้


หมายเหตุถึงผู้อ่าน: มีอ่านdocs.oracle.com/javase/tutorial/collections/interfaces/ ......อาจมีวิธีที่ง่ายกว่าในการบรรลุสิ่งที่คุณต้องการ
GKFX

คำตอบ:


1601

Iterator.remove() ปลอดภัยคุณสามารถใช้สิ่งนี้:

List<String> list = new ArrayList<>();

// This is a clever way to create the iterator and call iterator.hasNext() like
// you would do in a while-loop. It would be the same as doing:
//     Iterator<String> iterator = list.iterator();
//     while (iterator.hasNext()) {
for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
    String string = iterator.next();
    if (string.isEmpty()) {
        // Remove the current element from the iterator and the list.
        iterator.remove();
    }
}

โปรดทราบว่าIterator.remove()เป็นวิธีที่ปลอดภัยเพียงวิธีเดียวในการปรับเปลี่ยนคอลเล็กชันในระหว่างการทำซ้ำ พฤติกรรมดังกล่าวจะไม่ได้รับการระบุหากมีการแก้ไขคอลเล็กชันพื้นฐานด้วยวิธีอื่นใดในขณะที่การทำซ้ำอยู่ในระหว่างดำเนินการ

แหล่งที่มา: docs.oracle> The Collection Interface


และในทำนองเดียวกันหากคุณมีListIteratorและต้องการเพิ่มรายการคุณสามารถใช้ListIterator#addด้วยเหตุผลเดียวกับที่คุณสามารถใช้Iterator#remove - มันถูกออกแบบมาเพื่ออนุญาต


ในกรณีของคุณคุณพยายามที่จะลบออกจากรายการ แต่ข้อ จำกัด เดียวกันใช้ถ้าพยายามputเข้าไปMapในขณะที่ซ้ำเนื้อหา


19
ถ้าคุณต้องการลบองค์ประกอบอื่นที่ไม่ใช่องค์ประกอบที่ส่งคืนในการวนซ้ำปัจจุบัน
Eugen

2
คุณต้องใช้ .remove ใน iterator และที่เป็นเพียงสามารถลบองค์ประกอบปัจจุบันจึงไม่มี :)
บิล K

1
โปรดทราบว่านี่คือช้าลงเมื่อเทียบกับการใช้ ConcurrentLinkedDeque หรือ CopyOnWriteArrayList (อย่างน้อยในกรณีของฉัน)
แดน

1
ไม่สามารถวางiterator.next()สายใน for-loop ได้หรือไม่? ถ้าไม่มีใครสามารถอธิบายได้ว่าทำไม
Blake

1
@GonenI มันถูกใช้งานสำหรับตัววนซ้ำทั้งหมดจากคอลเลกชันซึ่งไม่เปลี่ยนรูป List.addเป็น "ทางเลือก" ในแง่เดียวกันเช่นกัน แต่คุณจะไม่พูดว่า "ไม่ปลอดภัย" เพื่อเพิ่มลงในรายการ
Radiodef

345

งานนี้:

Iterator<Integer> iter = l.iterator();
while (iter.hasNext()) {
    if (iter.next() == 5) {
        iter.remove();
    }
}

ฉันคิดว่าเนื่องจาก foreach loop เป็นน้ำตาลประโยคสำหรับการวนซ้ำการใช้ตัววนซ้ำจะไม่ช่วย ... แต่มันให้.remove()ฟังก์ชั่นนี้


43
foreach loop เป็นน้ำตาลประโยคสำหรับทำซ้ำ อย่างไรก็ตามในขณะที่คุณชี้ให้เห็นคุณจะต้องโทรลบบนตัววนซ้ำ - ซึ่ง foreach ไม่อนุญาตให้คุณเข้าถึง ดังนั้นเหตุผลที่คุณไม่สามารถลบในวง foreach (แม้ว่าคุณจะเป็นจริงโดยใช้ iterator ภายใต้ฝากระโปรง)
madlep

36
+1 สำหรับโค้ดตัวอย่างเพื่อใช้ iter.remove () ในบริบทซึ่งคำตอบของ Bill K ไม่ได้ [โดยตรง] มี
แก้ไขเมื่อ

202

ด้วย Java 8 คุณสามารถใช้วิธีการใหม่removeIfได้ นำไปใช้กับตัวอย่างของคุณ:

Collection<Integer> coll = new ArrayList<>();
//populate

coll.removeIf(i -> i == 5);

3
ooooo! ฉันหวังว่าบางสิ่งใน Java 8 หรือ 9 อาจช่วยได้ เรื่องนี้ดูเหมือนว่ายังค่อนข้าง verbose สำหรับฉัน แต่ฉันยังคงชอบมัน
James T Snell

การใช้งานเท่ากับ () แนะนำในกรณีนี้ด้วยหรือไม่
Anmol Gupta

โดยวิธีremoveIfใช้Iteratorและwhileวนรอบ คุณสามารถดูได้ที่ java 8java.util.Collection.java
omerhakanbilici

3
@omerhakanbilici การใช้งานบางอย่างเช่นArrayListแทนที่ด้วยเหตุผลด้านประสิทธิภาพ สิ่งที่คุณกำลังอ้างถึงเป็นเพียงการนำไปปฏิบัติเริ่มต้น
Didier L

@AnmolGupta: ไม่equalsไม่ได้ใช้งานที่นี่เลยดังนั้นจึงไม่ต้องดำเนินการ (แต่แน่นอนถ้าคุณใช้equalsในการทดสอบของคุณแล้วมันจะต้องมีการดำเนินการตามที่คุณต้องการ)
Lii

42

เนื่องจากคำถามได้รับการตอบแล้วเช่นวิธีที่ดีที่สุดคือการใช้วิธีการลบของวัตถุตัววนซ้ำฉันจะเข้าไปในข้อมูลเฉพาะของสถานที่ที่"java.util.ConcurrentModificationException"มีการโยนข้อผิดพลาด

ระดับคอลเลกชันทุกคนมีระดับเอกชนที่ดำเนิน interface Iterator และให้วิธีการเช่นnext(), และremove()hasNext()

โค้ดสำหรับสิ่งต่อไปมีลักษณะคล้ายนี้ ...

public E next() {
    checkForComodification();
    try {
        E next = get(cursor);
        lastRet = cursor++;
        return next;
    } catch(IndexOutOfBoundsException e) {
        checkForComodification();
        throw new NoSuchElementException();
    }
}

นี่คือวิธีการcheckForComodificationใช้งาน

final void checkForComodification() {
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}

ดังที่คุณเห็นหากคุณพยายามลบองค์ประกอบออกจากคอลเลกชัน ได้ผลในการmodCountได้รับแตกต่างจากผลในข้อยกเว้นexpectedModCountConcurrentModificationException


น่าสนใจมาก. ขอบคุณ! ฉันมักจะไม่โทรลบ () ตัวเองฉันแทนการล้างคอลเลกชันหลังจาก iterating ผ่านมันแทน อย่าบอกว่าเป็นรูปแบบที่ดีสิ่งที่ฉันทำเมื่อไม่นานมานี้
James T Snell

26

คุณสามารถใช้ตัววนซ้ำโดยตรงตามที่คุณกล่าวถึงหรือเก็บชุดที่สองไว้และเพิ่มแต่ละรายการที่คุณต้องการลบไปยังชุดสะสมใหม่จากนั้นลบทั้งหมดในตอนท้าย สิ่งนี้ช่วยให้คุณสามารถใช้ความปลอดภัยประเภทของ for-each loop ได้โดยเสียค่าใช้จ่ายในการใช้หน่วยความจำเพิ่มขึ้นและเวลา cpu (ไม่ควรเป็นปัญหาใหญ่เว้นแต่คุณจะมีรายการใหญ่จริงๆหรือคอมพิวเตอร์เก่าจริงๆ)

public static void main(String[] args)
{
    Collection<Integer> l = new ArrayList<Integer>();
    Collection<Integer> itemsToRemove = new ArrayList<>();
    for (int i=0; i < 10; i++) {
        l.add(Integer.of(4));
        l.add(Integer.of(5));
        l.add(Integer.of(6));
    }
    for (Integer i : l)
    {
        if (i.intValue() == 5) {
            itemsToRemove.add(i);
        }
    }

    l.removeAll(itemsToRemove);
    System.out.println(l);
}

7
นี่คือสิ่งที่ฉันทำตามปกติ แต่ตัววนซ้ำที่ชัดเจนเป็นวิธีแก้ปัญหาที่ยากยิ่งกว่าที่ฉันรู้สึก
Claudiu

1
ยุติธรรมเพียงพอตราบใดที่คุณไม่ได้ทำอะไรกับ iterator การเปิดใช้งานจะทำให้การทำสิ่งต่าง ๆ เช่น call. ถัดไป () สองครั้งต่อการวนซ้ำเป็นต้นไม่ใช่ปัญหาใหญ่ แต่อาจทำให้เกิดปัญหาหากคุณกำลังทำ สิ่งที่ซับซ้อนกว่าเพียงแค่เรียกใช้ผ่านรายการเพื่อลบรายการ
RodeoClown

@RodeoClown: ในคำถามเดิม Claudiu กำลังนำออกจากคอลเล็กชันไม่ใช่ตัววนซ้ำ
matt b

1
การลบออกจากตัววนซ้ำลบออกจากการรวบรวมพื้นฐาน ... แต่สิ่งที่ฉันพูดในความคิดเห็นสุดท้ายคือถ้าคุณกำลังทำอะไรที่ซับซ้อนมากกว่าเพียงแค่มองหาการลบในลูป (เช่นการประมวลผลข้อมูลที่ถูกต้อง) โดยใช้ตัววนซ้ำ ข้อผิดพลาดง่ายต่อการทำ
RodeoClown

หากเป็นค่าลบอย่างง่ายที่ไม่จำเป็นและลูปจะทำสิ่งนั้นเพียงอย่างเดียวการใช้ตัววนซ้ำโดยตรงและการเรียก. Remove () นั้นใช้ได้จริง
RodeoClown

17

ในกรณีเช่นนี้กลอุบายทั่วไปคือ (เคยเป็น) เพื่อย้อนกลับ:

for(int i = l.size() - 1; i >= 0; i --) {
  if (l.get(i) == 5) {
    l.remove(i);
  }
}

ที่กล่าวว่าฉันมีความสุขมากกว่าที่คุณมีวิธีที่ดีกว่าใน Java 8 เช่นremoveIfหรือfilterบนสตรีม


2
นี่เป็นเคล็ดลับที่ดี แต่มันจะไม่ทำงานในคอลเลกชันที่ไม่ได้จัดทำดัชนีเช่นชุดและมันจะช้ามากในรายการพูดที่เชื่อมโยง
Claudiu

@Claudiu ใช่นี่เป็นเพียงสำหรับArrayListคอลเลกชันที่คล้ายกัน
Landei

ฉันใช้ ArrayList มันใช้ได้ผลดีมากขอบคุณ
StarSweeper

2
ดัชนีดีมาก หากเป็นเรื่องทั่วไปทำไมคุณไม่ใช้for(int i = l.size(); i-->0;) {?
จอห์น


12

ด้วยEclipse Collectionsวิธีที่removeIfกำหนดไว้ในMutableCollectionจะทำงาน:

MutableList<Integer> list = Lists.mutable.of(1, 2, 3, 4, 5);
list.removeIf(Predicates.lessThan(3));
Assert.assertEquals(Lists.mutable.of(3, 4, 5), list);

ด้วย Java 8 Lambda ไวยากรณ์นี้สามารถเขียนได้ดังนี้:

MutableList<Integer> list = Lists.mutable.of(1, 2, 3, 4, 5);
list.removeIf(Predicates.cast(integer -> integer < 3));
Assert.assertEquals(Lists.mutable.of(3, 4, 5), list);

Predicates.cast()จำเป็นต้องเรียกใช้ที่นี่เนื่องจากมีremoveIfการเพิ่มวิธีการเริ่มต้นบนjava.util.Collectionอินเทอร์เฟซใน Java 8

หมายเหตุ:ผม committer สำหรับEclipse คอลเลกชัน


10

ทำสำเนาของรายการที่มีอยู่แล้ววนซ้ำสำเนาใหม่

for (String str : new ArrayList<String>(listOfStr))     
{
    listOfStr.remove(/* object reference or index */);
}

19
การทำสำเนาดูเหมือนจะเป็นการสิ้นเปลืองทรัพยากร
Antzi

3
@Antzi ขึ้นอยู่กับขนาดของรายการและความหนาแน่นของวัตถุภายใน ยังคงเป็นโซลูชันที่มีคุณค่าและถูกต้อง
mre

ฉันใช้วิธีนี้ ใช้ทรัพยากรเพิ่มขึ้นเล็กน้อย แต่มีความยืดหยุ่นและชัดเจนมากขึ้น
เต๋าจางจาง

นี่เป็นทางออกที่ดีเมื่อคุณไม่ต้องการลบวัตถุภายในลูป แต่พวกมันค่อนข้างถูก "สุ่ม" ออกจากเธรดอื่น ๆ (เช่นการอัปเดตการดำเนินงานเครือข่าย) หากคุณพบว่าตัวเองกำลังทำสำเนาเหล่านี้จำนวนมากก็ยังมีการนำ Java ไปใช้ให้ทำเช่นนี้: docs.oracle.com/javase/8/docs/api/java/util/concurrent/ ......
A1m

8

ผู้คนยืนยันว่าไม่สามารถลบออกจากคอลเล็กชันที่วนซ้ำ foreach ฉันแค่อยากจะชี้ให้เห็นว่ามันไม่ถูกต้องทางเทคนิคและอธิบายอย่างแน่นอน (ฉันรู้ว่าคำถามของ OP นั้นล้ำหน้าไปมากเมื่อต้องรู้เรื่องนี้) รหัสหลังสมมติฐาน:

for (TouchableObj obj : untouchedSet) {  // <--- This is where ConcurrentModificationException strikes
    if (obj.isTouched()) {
        untouchedSet.remove(obj);
        touchedSt.add(obj);
        break;  // this is key to avoiding returning to the foreach
    }
}

ไม่ใช่ว่าคุณไม่สามารถลบออกจากการทำซ้ำได้Colletionแต่จะไม่สามารถทำซ้ำได้เมื่อคุณทำแล้ว ดังนั้นbreakในรหัสข้างต้น

ขออภัยหากคำตอบนี้เป็นกรณีใช้งานที่เชี่ยวชาญและเหมาะกับเธรดต้นฉบับที่ฉันมาถึงจากที่นี่คำตอบนั้นถูกทำเครื่องหมายว่าซ้ำกัน


8

ด้วยแบบดั้งเดิมสำหรับวง

ArrayList<String> myArray = new ArrayList<>();

for (int i = 0; i < myArray.size(); ) {
    String text = myArray.get(i);
    if (someCondition(text))
        myArray.remove(i);
    else
        i++;   
}

อ่าดังนั้นมันจึงเป็นเพียงการปรับปรุงสำหรับวนซ้ำที่ทำให้เกิดข้อยกเว้น
cellepo

FWIW - รหัสเดียวกันจะยังคงทำงานหลังจากแก้ไขเพื่อเพิ่มขึ้นi++ในการป้องกันวงมากกว่าภายในร่างกายห่วง
cellepo

การแก้ไข ^: นั่นคือหากการi++เพิ่มขึ้นไม่เป็นไปตามเงื่อนไข - ฉันเห็นแล้วตอนนี้นั่นเป็นสาเหตุที่คุณทำมันในร่างกาย :)
cellepo

2

ListIteratorช่วยให้คุณสามารถเพิ่มหรือลบรายการในรายการ สมมติว่าคุณมีรายการCarวัตถุ:

List<Car> cars = ArrayList<>();
// add cars here...

for (ListIterator<Car> carIterator = cars.listIterator();  carIterator.hasNext(); )
{
   if (<some-condition>)
   { 
      carIterator().remove()
   }
   else if (<some-other-condition>)
   { 
      carIterator().add(aNewCar);
   }
}

วิธีการเพิ่มเติมในอินเทอร์เฟซListIterator (ส่วนขยายของ Iterator) น่าสนใจ - โดยเฉพาะอย่างยิ่งpreviousวิธีการ
cellepo

1

ฉันมีข้อเสนอแนะสำหรับปัญหาข้างต้น ไม่จำเป็นต้องมีรายชื่อสำรองหรือเวลาพิเศษใด ๆ กรุณาค้นหาตัวอย่างที่จะทำสิ่งเดียวกัน แต่ในวิธีที่แตกต่างกัน

//"list" is ArrayList<Object>
//"state" is some boolean variable, which when set to true, Object will be removed from the list
int index = 0;
while(index < list.size()) {
    Object r = list.get(index);
    if( state ) {
        list.remove(index);
        index = 0;
        continue;
    }
    index += 1;
}

สิ่งนี้จะหลีกเลี่ยงข้อยกเว้นการเกิดพร้อมกัน


1
คำถามระบุอย่างชัดเจนว่า OP ไม่จำเป็นต้องใช้ArrayListและไม่สามารถget()เชื่อถือได้ มิฉะนั้นอาจเป็นวิธีที่ดี
kaskelotti

(ชี้แจง ^) OP ใช้โดยพลการCollection- อินเตอร์เฟซที่ไม่รวมถึงCollection get(แม้ว่าListส่วนต่อประสานFWIW จะรวม 'รับ')
cellepo

ฉันเพิ่งเพิ่มแยกคำตอบรายละเอียดเพิ่มเติมได้ที่นี่ยังwhile-looping Listแต่ +1 สำหรับคำตอบนี้เพราะมันมาก่อน
cellepo

1

ConcurrentHashMapหรือConcurrentLinkedQueueหรือConcurrentSkipListMapอาจเป็นอีกทางเลือกหนึ่งเพราะพวกเขาจะไม่ทิ้ง ConcurrentModificationException ใด ๆ แม้ว่าคุณจะลบหรือเพิ่มรายการก็ตาม


ใช่และโปรดทราบว่าสิ่งเหล่านั้นทั้งหมดอยู่ในjava.util.concurrentแพ็คเกจ คลาสที่คล้ายกัน / ใช้งานทั่วไปกรณีอื่น ๆ จากแพ็คเกจนั้นCopyOnWriteArrayList & CopyOnWriteArraySet [แต่ไม่ จำกัด เฉพาะคลาส]
cellepo

ที่จริงแล้วฉันเพิ่งเรียนรู้ว่าถึงแม้โครงสร้างข้อมูลเหล่านั้นจะหลีกเลี่ยง วัตถุConcurrentModificationExceptionแต่การใช้พวกมันใน-for-loop ที่ปรับปรุงแล้วยังสามารถทำให้เกิดปัญหาการทำดัชนีได้ (เช่น: ยังคงข้ามองค์ประกอบหรือIndexOutOfBoundsException... )
cellepo

1

ฉันรู้ว่าคำถามนี้เก่าเกินไปที่จะเกี่ยวกับ Java 8 แต่สำหรับผู้ที่ใช้ Java 8 คุณสามารถใช้ removeIf ():

Collection<Integer> l = new ArrayList<Integer>();

for (int i=0; i < 10; ++i) {
    l.add(new Integer(4));
    l.add(new Integer(5));
    l.add(new Integer(6));
}

l.removeIf(i -> i.intValue() == 5);

1

อีกวิธีหนึ่งคือการสร้างสำเนาของ arrayList ของคุณ:

List<Object> l = ...

List<Object> iterationList = ImmutableList.copyOf(l);

for (Object i : iterationList) {
    if (condition(i)) {
        l.remove(i);
    }
}

หมายเหตุ: iไม่ใช่indexแต่เป็นวัตถุแทน อาจจะเรียกว่าobjเหมาะสมกว่า
luckydonald

1

วิธีที่ดีที่สุด (แนะนำ) คือการใช้แพ็คเกจ java.util.Concurrent ด้วยการใช้แพ็คเกจนี้คุณสามารถหลีกเลี่ยงข้อยกเว้นนี้ได้อย่างง่ายดาย อ้างอิง Modified Code

public static void main(String[] args) {
    Collection<Integer> l = new CopyOnWriteArrayList<Integer>();

    for (int i=0; i < 10; ++i) {
        l.add(new Integer(4));
        l.add(new Integer(5));
        l.add(new Integer(6));
    }

    for (Integer i : l) {
        if (i.intValue() == 5) {
            l.remove(i);
        }
    }

    System.out.println(l);
}

0

ในกรณีArrayList: remove (int index) - ถ้า (index เป็นตำแหน่งสุดท้ายขององค์ประกอบ) มันหลีกเลี่ยงโดยไม่ต้องSystem.arraycopy()ใช้เวลา

เวลา arraycopy เพิ่มขึ้นหาก (ดัชนีลดลง) โดยวิธีที่องค์ประกอบของรายการลดลงด้วย!

วิธีลบที่มีประสิทธิภาพดีที่สุดคือ - ลบองค์ประกอบในลำดับถัดลงมา: while(list.size()>0)list.remove(list.size()-1);// ใช้ O (1) while(list.size()>0)list.remove(0);// ใช้ O (แฟคทอเรียล (n))

//region prepare data
ArrayList<Integer> ints = new ArrayList<Integer>();
ArrayList<Integer> toRemove = new ArrayList<Integer>();
Random rdm = new Random();
long millis;
for (int i = 0; i < 100000; i++) {
    Integer integer = rdm.nextInt();
    ints.add(integer);
}
ArrayList<Integer> intsForIndex = new ArrayList<Integer>(ints);
ArrayList<Integer> intsDescIndex = new ArrayList<Integer>(ints);
ArrayList<Integer> intsIterator = new ArrayList<Integer>(ints);
//endregion

// region for index
millis = System.currentTimeMillis();
for (int i = 0; i < intsForIndex.size(); i++) 
   if (intsForIndex.get(i) % 2 == 0) intsForIndex.remove(i--);
System.out.println(System.currentTimeMillis() - millis);
// endregion

// region for index desc
millis = System.currentTimeMillis();
for (int i = intsDescIndex.size() - 1; i >= 0; i--) 
   if (intsDescIndex.get(i) % 2 == 0) intsDescIndex.remove(i);
System.out.println(System.currentTimeMillis() - millis);
//endregion

// region iterator
millis = System.currentTimeMillis();
for (Iterator<Integer> iterator = intsIterator.iterator(); iterator.hasNext(); )
    if (iterator.next() % 2 == 0) iterator.remove();
System.out.println(System.currentTimeMillis() - millis);
//endregion
  • สำหรับดัชนีลูป: 1090 msec
  • สำหรับดัชนีเรียง: 519 msec --- ดีที่สุด
  • สำหรับ iterator: 1043 msec

0
for (Integer i : l)
{
    if (i.intValue() == 5){
            itemsToRemove.add(i);
            break;
    }
}

การจับเป็นการลบองค์ประกอบออกจากรายการหากคุณข้ามการเรียก iterator.next () ภายใน มันยังใช้งานได้! แม้ว่าฉันจะไม่เสนอให้เขียนโค้ดแบบนี้มันช่วยให้เข้าใจแนวคิดเบื้องหลัง :-)

ไชโย!


0

ตัวอย่างของการปรับเปลี่ยนคอลเลกชันที่ปลอดภัยเธรด:

public class Example {
    private final List<String> queue = Collections.synchronizedList(new ArrayList<String>());

    public void removeFromQueue() {
        synchronized (queue) {
            Iterator<String> iterator = queue.iterator();
            String string = iterator.next();
            if (string.isEmpty()) {
                iterator.remove();
            }
        }
    }
}

0

ฉันรู้ว่าคำถามนี้ถือว่าเพียงและไม่มากขึ้นโดยเฉพาะใด ๆCollection Listแต่สำหรับผู้ที่อ่านคำถามนี้ซึ่งทำงานกับการListอ้างอิงคุณสามารถหลีกเลี่ยงConcurrentModificationExceptionด้วยwhile-loop (ในขณะที่แก้ไขภายใน) แทนหากคุณต้องการหลีกเลี่ยงIterator (เช่นถ้าคุณต้องการหลีกเลี่ยงโดยทั่วไปหรือหลีกเลี่ยงโดยเฉพาะเพื่อให้บรรลุ ลำดับการวนซ้ำแตกต่างจากการหยุดตั้งแต่ต้นจนจบที่แต่ละองค์ประกอบ [ซึ่งฉันเชื่อว่าเป็นคำสั่งเดียวที่Iteratorทำได้)]:

* อัปเดต: ดูความคิดเห็นด้านล่างที่อธิบายความคล้ายคลึงกันนั้นสามารถทำได้ด้วย-for-loop แบบดั้งเดิม

final List<Integer> list = new ArrayList<>();
for(int i = 0; i < 10; ++i){
    list.add(i);
}

int i = 1;
while(i < list.size()){
    if(list.get(i) % 2 == 0){
        list.remove(i++);

    } else {
        i += 2;
    }
}

ไม่มี ConcurrentModificationException จากรหัสนั้น

ที่นั่นเราเห็นลูปไม่เริ่มที่จุดเริ่มต้นและไม่หยุดทุกองค์ประกอบ (ซึ่งฉันเชื่อIteratorตัวเองไม่สามารถทำได้)

FWIW เราเห็นด้วยเช่นกัน getว่ามีการเรียกใช้listซึ่งไม่สามารถทำได้ถ้าการอ้างอิงของมันเป็นเพียงแค่Collection(แทนที่จะเป็นListชนิดที่เฉพาะเจาะจงมากขึ้นCollection) - Listอินเตอร์เฟสรวมถึงgetแต่Collectionอินเตอร์เฟสไม่ได้ หากไม่ใช่ความแตกต่างนั้นการlistอ้างอิงอาจเป็นCollection [และดังนั้นในทางเทคนิคแล้วคำตอบนี้จะเป็นคำตอบโดยตรงแทนที่จะเป็นคำตอบแทนเจนต์]

รหัสเดียวกันของ FWIWW ยังคงทำงานหลังจากแก้ไขเพื่อเริ่มต้นที่จุดเริ่มต้นที่ทุกองค์ประกอบ (เช่นเดียวกับการIteratorสั่งซื้อ):

final List<Integer> list = new ArrayList<>();
for(int i = 0; i < 10; ++i){
    list.add(i);
}

int i = 0;
while(i < list.size()){
    if(list.get(i) % 2 == 0){
        list.remove(i);

    } else {
        ++i;
    }
}

สิ่งนี้ยังคงต้องใช้การคำนวณตัวบ่งชี้เพื่อลบอย่างระมัดระวังอย่างไรก็ตาม
OneCricketeer

นอกจากนี้นี่เป็นเพียงคำอธิบายรายละเอียดเพิ่มเติมของคำตอบนี้stackoverflow.com/a/43441822/2308683
OneCricketeer

เป็นการดีที่จะรู้ - ขอบคุณ! ว่าคำตอบอื่น ๆ ที่ช่วยให้ฉันเข้าใจว่ามันเป็นเพิ่มเผื่อห่วงว่าจะโยนConcurrentModificationExceptionแต่ไม่ดั้งเดิมเผื่อวง (ซึ่งใช้คำตอบอื่น ๆ ) - ไม่ทราบว่าก่อนที่จะเป็นเหตุผลที่ผมเป็นแรงบันดาลใจในการเขียนคำตอบนี้ (ฉันไม่สมควร คิดแล้วว่ามันคือทั้งหมดสำหรับลูปที่จะโยนข้อยกเว้น)
cellepo

0

วิธีหนึ่งที่สามารถหมุนเวียนรายการและลบองค์ประกอบแรกเพื่อหลีกเลี่ยงการ ConcurrentModificationException หรือ IndexOutOfBoundsException

int n = list.size();
for(int j=0;j<n;j++){
    //you can also put a condition before remove
    list.remove(0);
    Collections.rotate(list, 1);
}
Collections.rotate(list, -1);

0

ลองอันนี้ (ลบองค์ประกอบทั้งหมดในรายการที่เท่ากันi):

for (Object i : l) {
    if (condition(i)) {
        l = (l.stream().filter((a) -> a != i)).collect(Collectors.toList());
    }
}

0

คุณยังสามารถใช้การเรียกซ้ำ

การเรียกซ้ำใน java เป็นกระบวนการที่เมธอดเรียกตัวเองอย่างต่อเนื่อง เมธอดใน java ที่เรียกตัวเองว่าเมธอดแบบเรียกซ้ำ


-2

นี่อาจไม่ใช่วิธีที่ดีที่สุด แต่สำหรับกรณีเล็ก ๆ ส่วนใหญ่ควรยอมรับได้:

"สร้างอาร์เรย์ว่างอันที่สองและเพิ่มเฉพาะอาร์เรย์ที่คุณต้องการเก็บไว้"

ฉันไม่จำที่ฉันอ่านข้อความนี้ได้จาก ... เพราะความยุติธรรมฉันจะทำให้วิกินี้ด้วยความหวังว่ามีคนพบมันหรือเพียงแค่ไม่ได้รับตัวแทนฉันไม่สมควรได้รับ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.