DevLog ๐Ÿ˜ถ

[Java] ConcurrentModificationException์€ ์–ธ์ œ ๋ฐœ์ƒํ• ๊นŒ? iterator์˜ ๋™์ž‘ ์›๋ฆฌ ์‚ดํŽด๋ณด๊ธฐ ๋ณธ๋ฌธ

โœ๏ธ/Java

[Java] ConcurrentModificationException์€ ์–ธ์ œ ๋ฐœ์ƒํ• ๊นŒ? iterator์˜ ๋™์ž‘ ์›๋ฆฌ ์‚ดํŽด๋ณด๊ธฐ

dolmeng2 2023. 3. 6. 22:02

๐Ÿ’ฌ ๋ชจ๋˜ ์ž๋ฐ”๋ฅผ ๊ณต๋ถ€ํ•˜๋Š” ๋„์ค‘์—, ์ฑ…์—์„œ ๋‚˜์™”๋˜ ์˜ˆ์ œ๋ฅผ ์ปค์Šคํ…€ ํ•˜๋ ค๋‹ค๊ฐ€ ์ด๋Ÿฐ ์ƒํ™ฉ์„ ๋งŒ๋‚ฌ๋‹ค.


โœ”๏ธ ๋ฌธ์ œ ์ƒํ™ฉ

๋จผ์ €, ์‚ฌ์šฉํ•œ ํฌ๋ฃจ ํด๋ž˜์Šค์— ๋Œ€ํ•œ ์ •๋ณด์ด๋‹ค.

public class Crew {
    private String name;
    private String nickname;
    private int age;
    private Course course;

    private Crew() {
        // ์™ธ๋ถ€์—์„œ ์ธ์Šคํ„ด์Šคํ™” ๋ง‰๊ธฐ
    }

    public static Crew createByName(final String name, final int age, final Course course) {
        Crew crew = new Crew();
        crew.name = name;
        crew.age = age;
        crew.course = course;
        return crew;
    }

    public static Crew createByNickname(final String nickname, final int age, final Course course) {
        Crew crew = new Crew();
        crew.nickname = nickname;
        crew.age = age;
        crew.course = course;
        return crew;
    }

    public int getAge() {
        return age;
    }

    public Course getCourse() {
        return course;
    }

    public String getNickname() {
        return nickname;
    }
}

์ดํ›„, ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ ์ƒํ™ฉ์ด๋‹ค. ์ฑ…์—์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒฝ์šฐ์— ๋Œ€ํ•ด์„œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค๊ณ  ํ•˜์˜€๋‹ค.

List<Crew> crews = new ArrayList<>();
crews.add(Crew.createByNickname("hi", 20, Course.BACKEND));
crews.add(Crew.createByNickname("hello", 21, Course.BACKEND));
crews.add(Crew.createByNickname("hihi", 22, Course.BACKEND));

for (Crew crew : crews) {
    if (crew.getAge() == 20) {
        crews.remove(crew);
    }
}

 

์ด ๊ฒฝ์šฐ์—์„œ๋Š” ๋‹น์—ฐํ•˜๊ฒŒ๋„ ConcurrentModificationException์ด ๋ฐœ์ƒํ•œ๋‹ค.


 

โœ”๏ธ ConcurrentModificationException์ด๋ž€?

๋จผ์ €, ConcurrentModificationException์ด๋ž€, ์ž๋ฐ”์—์„œ ์ปฌ๋ ‰์…˜ ๋‚ด๋ถ€์˜ ์š”์†Œ๋ฅผ ๋™์‹œ์— ์ˆ˜์ •ํ•˜๋ ค๊ณ  ํ•  ๋•Œ ๋ฐœ์ƒํ•œ๋‹ค.

๊ธฐ๋ณธ์ ์œผ๋กœ ์ž๋ฐ”์˜ ์ปฌ๋ ‰์…˜ ํ”„๋ ˆ์ž„์›Œํฌ๋Š” Iterator๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ปฌ๋ ‰์…˜์˜ ์š”์†Œ๋ฅผ ๋ฐ˜๋ณตํ•˜๋Š”๋ฐ, ์ด๋•Œ ์ปฌ๋ ‰์…˜์„ ์ˆ˜์ •ํ•˜๋ฉด์„œ ๋™์‹œ์— ๋ฐ˜๋ณต์„ ๋Œ๊ฒŒ ๋˜๋ฉด ํ•ด๋‹น ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ๋ณดํ†ต์˜ ๊ฒฝ์šฐ๋Š” ๋‹ค์ค‘ ์Šค๋ ˆ๋“œ์—์„œ ๋™์‹œ์— ์ˆ˜์ •ํ•  ๋•Œ ๋งŽ์ด ๋ฐœ์ƒํ•˜์ง€๋งŒ, ๋‹จ์ผ ์Šค๋ ˆ๋“œ์—์„œ๋„ ์œ„์™€ ๊ฐ™์ด ์ปฌ๋ ‰์…˜์˜ ์š”์†Œ๋ฅผ ์ˆœํšŒํ•˜๋Š” ๋™์‹œ์—, ํ•ด๋‹น ์ปฌ๋ ‰์…˜์˜ ์š”์†Œ๋ฅผ ์ œ๊ฑฐํ•˜๊ฒŒ ๋˜๋ฉด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.

 

์™œ ์ด๋Ÿฐ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋Š”์ง€๋Š” ์•„๋ž˜์—์„œ ์‚ดํŽด๋ณด๊ณ , ๋‹ค์Œ์˜ ์˜ˆ์ œ๋ฅผ ์ƒ๊ฐํ•ด๋ณด์ž.

List<Crew> crews = new ArrayList<>();
crews.add(Crew.createByNickname("hi", 20, Course.BACKEND));
crews.add(Crew.createByNickname("hello", 21, Course.BACKEND));

for (Crew crew : crews) {
    if (crew.getAge() == 20) {
        crews.remove(crew);
    }
}

์œ„ ์ฝ”๋“œ์—์„œ crews.add()๋ฅผ ํ•˜๋Š” ์ž‘์—…์„ ํ•œ ์ค„ ์ œ๊ฑฐํ–ˆ์„ ๋ฟ์ด๊ธฐ ๋•Œ๋ฌธ์—, ์ด ์ฝ”๋“œ ์—ญ์‹œ ๋‹น์—ฐํ•˜๊ฒŒ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•  ๊ฒƒ์ด๋‹ค.

ํ•˜์ง€๋งŒ, ๋†€๋ž๊ฒŒ๋„ ์•„๋ฌด ์˜ค๋ฅ˜๋„ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค... (???)

์ฒ˜์Œ์— ์ด๊ฑธ ๋ณด๊ณ  ๋Œ€์ฒด ์™œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ผ๊นŒ, ์—„์ฒญ ๊ณ ๋ฏผํ–ˆ์—ˆ๋Š”๋ฐ ์ด๋Š” for-each์˜ ๋™์ž‘ ๊ณผ์ •์„ ์ดํ•ดํ•˜๋ฉด ๋œ๋‹ค.

 


โœ”๏ธ Iterator ์ดํ•ดํ•˜๊ธฐ

์šฐ์„ , ์ฒซ ๋ฒˆ์งธ ์ผ€์ด์Šค๋Š” ์™œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์ผ๊นŒ? ํ•ด๋‹น ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด Iterator๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ๋ณ€๊ฒฝ๋œ๋‹ค.

List<Crew> crews = new ArrayList<>();
crews.add(Crew.createByNickname("hi", 20, Course.BACKEND));
crews.add(Crew.createByNickname("hello", 21, Course.BACKEND));
crews.add(Crew.createByNickname("hihi", 22, Course.BACKEND));

for (Iterator<Crew> iterator = crews.listIterator(); iterator.hasNext();) {
    Crew crew = iterator.next();
    if (crew.getAge() == 20) {
        crews.remove(crew);
    }
}

 

์ด๋•Œ, iterator.next()๋ฅผ ์ง„ํ–‰ํ•˜๋ฉด์„œ ๋‹ค์Œ ์š”์†Œ๋ฅผ ๊ฐ€์ ธ์˜ค๊ฒŒ ๋˜๋Š”๋ฐ, next()๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ตฌํ˜„๋˜์–ด ์žˆ๋‹ค.

public E next() {
    checkForComodification();
    int i = cursor;
    if (i >= size)
        throw new NoSuchElementException();
    Object[] elementData = ArrayList.this.elementData;
    if (i >= elementData.length)
        throw new ConcurrentModificationException();
    cursor = i + 1;
    return (E) elementData[lastRet = i];
}

 

์—ฌ๊ธฐ์„œ, ๊ฐ€์žฅ ์ƒ๋‹จ์˜ checkForModification() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ํ•ด๋‹น ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ์–ด๋– ํ•œ ์ž‘์—…์„ ํ•˜๊ณ , (๊ทธ๊ฒƒ์€ ์•„๋ž˜์—์„œ ์„ค๋ช…ํ•  ์˜ˆ์ •์ด๋‹ค!) ํ•ด๋‹น ์š”์†Œ์˜ ์ธ๋ฑ์Šค์— ๋Œ€ํ•ด์„œ cursor๋ฅผ ์„ค์ •ํ•˜๊ฒŒ ๋œ๋‹ค. ๊ทธ๋ฆฌ๊ณ , ๋งŒ์•ฝ ์ธ๋ฑ์Šค๊ฐ€ ์‚ฌ์ด์ฆˆ๋ณด๋‹ค ํฌ๋ฉด ๋‹ค์Œ ์š”์†Œ๊ฐ€ ์—†์Œ์„ ๋‚˜ํƒ€๋‚ด๋ฉฐ, elementData์—์„œ ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€์„œ cursor๋Š” ๊ทธ ๋‹ค์Œ ์š”์†Œ์˜ ์ธ๋ฑ์Šค๋ฅผ, lastRet์—๋Š” ํ˜„์žฌ ์š”์†Œ์˜ ์ธ๋ฑ์Šค๋ฅผ ์ €์žฅํ•˜๊ฒŒ ๋œ๋‹ค.

 

 

๐Ÿ’ฌ checkForComodification()

๋จผ์ €, checkForComodification()์— ๋Œ€ํ•ด์„œ ์‚ดํŽด๋ณด์ž. 

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

modCount์™€ expectedModCount๊ฐ€ ๋ฌด์—‡์ผ๊นŒ?

modCount๋Š” ModificationCount๋กœ, ํ•ด๋‹น ๋ฆฌ์ŠคํŠธ์— ๋Œ€ํ•œ ์ˆ˜์ •๋œ ํšŸ์ˆ˜๋ฅผ ๊ด€๋ฆฌํ•œ๋‹ค. ํ•ด๋‹น ํ•„๋“œ๊ฐ€ ๊ตฌํ˜„๋œ ๊ณณ์— ๊ฐ€์„œ ์ฃผ์„์„ ์‚ดํŽด๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์„ฑ๋˜์–ด ์žˆ๋‹ค.

 

The number of times this list has been structurally modified.
If the value of this field changes unexpectedly, the iterator (or list iterator) will throw a ConcurrentModificationException in response to the next, remove, previous, set or add operations.

๊ตฌ์กฐ์ ์œผ๋กœ ์ˆ˜์ •๋œ ํšŸ์ˆ˜์— ๋Œ€ํ•ด์„œ ๊ด€๋ฆฌํ•˜๋ฉฐ, ์˜ˆ๊ธฐ์น˜ ์•Š๊ฒŒ ๋ณ€๊ฒฝ์ด ๋˜์—ˆ์„ ๊ฒฝ์šฐ ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚จ๋‹ค๊ณ  ๋‚˜์™€ ์žˆ๋‹ค. next(), remove(), previous() ๋“ฑ์˜ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ๋งˆ๋‹ค modCount๊ฐ€ 1์”ฉ ์ฆ๊ฐ€ํ•˜๊ฒŒ ๋œ๋‹ค.

 

๋ฐ˜๋ฉด์—, expectedModCount๋Š” ArrayList์˜ ๋ฐ˜๋ณต์ž(iterator)๊ฐ€ ์ƒ์„ฑ๋  ๋•Œ์˜ modCount๋ฅผ ์ €์žฅํ•œ๋‹ค.

public Iterator<E> iterator() {
	return new Itr();
}

/**
 * An optimized version of AbstractList.Itr
 */
private class Itr implements Iterator<E> {
    int cursor;       // index of next element to return
    int lastRet = -1; // index of last element returned; -1 if no such
    int expectedModCount = modCount;

    // prevent creating a synthetic constructor
    Itr() {}
    ...
}

์‹ค์ œ๋กœ ๋‚ด๋ถ€ ๊ตฌํ˜„์„ ๋ณด๋ฉด, Itr๋ผ๋Š” ํด๋ž˜์Šค์—์„œ expectedModCount์— ๋Œ€ํ•ด modCount๋กœ ์ดˆ๊ธฐ๊ฐ’์„ ์ง€์ •ํ•ด๋‘” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

 

์ฆ‰, modCount๋Š” ๋ฐ˜๋ณต์ž์— ๊ด€๋ จ๋œ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ๋งˆ๋‹ค, expectedModCount๋Š” ๊ฐ€์žฅ ์ฒ˜์Œ ์ƒ์„ฑํ•  ๋•Œ์˜ modCount๋ฅผ ์ €์žฅํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ์ดˆ๊ธฐ ์ƒํƒœ๋ฅผ ์ €์žฅํ•ด๋‘๊ณ , ์ด๋ฅผ ๋น„๊ตํ•˜๋ฉด์„œ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์ดˆ๊ธฐ ์ƒํƒœ์—์„œ ๋‹ฌ๋ผ์กŒ๊ธฐ ๋•Œ๋ฌธ์— ํ˜„์žฌ ์ƒํƒœ๊ฐ€ ๋™์ผํ•˜์ง€ ์•Š๋‹ค๋Š” ์˜๋ฏธ๋กœ concurrentModficiationException์ด ๋ฐœ์ƒํ•˜๊ฒŒ ๋˜๋Š” ๊ฒƒ์ด๋‹ค.

 

๊ทธ๋ ‡๋‹ค๋ฉด, ๋‹ค์‹œ ์šฐ๋ฆฌ์˜ ๊ตฌํ˜„์ฒด๋ฅผ ๋ณด์ž. ํ˜„์žฌ ์šฐ๋ฆฌ๋Š” if ์กฐ๊ฑด๋ฌธ์— ์ผ์น˜ํ•˜๋Š” ์š”์†Œ์— ๋Œ€ํ•ด์„œ ์ œ๊ฑฐ ์ž‘์—…์„ ์ง„ํ–‰ํ•˜๊ณ  ์žˆ๋‹ค.

ํ˜„์žฌ ํฌ๋ฃจ ๋ฆฌ์ŠคํŠธ์—๋Š” ๋‚˜์ด๊ฐ€ 20์ธ ํฌ๋ฃจ๊ฐ€ ์กด์žฌํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ , ํ•ด๋‹น ํฌ๋ฃจ์— ๋Œ€ํ•ด ์ œ๊ฑฐํ•˜๋Š” ์ž‘์—…์„ ์ง„ํ–‰ํ•˜๊ฒŒ ๋œ๋‹ค.

    public boolean remove(Object o) {
        final Object[] es = elementData;
        final int size = this.size;
        int i = 0;
        found: {
            if (o == null) {
                for (; i < size; i++)
                    if (es[i] == null)
                        break found;
            } else {
                for (; i < size; i++)
                    if (o.equals(es[i]))
                        break found;
            }
            return false;
        }
        fastRemove(es, i);
        return true;
    }

remove ๋ฉ”์„œ๋“œ๋Š” ์ œ๊ฑฐํ•  ์š”์†Œ์˜ ์ธ๋ฑ์Šค๋ฅผ ์ฐพ์•„๋‚ธ ๋‹ค์Œ, ํ•ด๋‹น ์ธ๋ฑ์Šค์— ์žˆ๋Š” ๊ฐ’์„ ์ œ๊ฑฐํ•œ๋‹ค. ์ด๋•Œ, elementData๋Š” ArrayList์—์„œ ๊ด€๋ฆฌํ•˜๋Š” ๋‚ด๋ถ€ ๋ฐฐ์—ด์ด๊ณ  (๋ฆฌ์ŠคํŠธ๋Š” ์›๋ž˜ ๋ฐฐ์—ด๋กœ ๊ด€๋ฆฌ๋œ๋‹ค) size๋Š” ํ˜„์žฌ ์ €์žฅ๋œ ์š”์†Œ์˜ ๊ฐœ์ˆ˜๋ฅผ ๋‚˜ํƒ€๋‚ธ๋‹ค. ๋งŒ์•ฝ ์š”์†Œ๊ฐ€ null์ด๋ผ๋ฉด ==๋กœ, null์ด ์•„๋‹ˆ๋ผ๋ฉด equals๋กœ ๋น„๊ตํ•˜๊ฒŒ ๋˜๋Š”๋ฐ, for๋ฌธ์„ ๋Œ๋ฉด์„œ ์ œ๊ฑฐํ•  ์š”์†Œ๋ฅผ ์ฐพ์•„๋‚ด๋ฉด ํ•ด๋‹น ์š”์†Œ์˜ ์ธ๋ฑ์Šค๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ fastRemove() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.

 

private void fastRemove(Object[] es, int i) {
    modCount++;
    final int newSize;
    if ((newSize = size - 1) > i)
        System.arraycopy(es, i + 1, es, i, newSize - i);
    es[size = newSize] = null;
}

์ด๋•Œ, fastRemove ๋ฉ”์„œ๋“œ๋ฅผ ๋ณด๋ฉด ์ต์ˆ™ํ•œ ๋ฌด์–ธ๊ฐ€๊ฐ€ ๋ณด์ธ๋‹ค. ๋ฐ”๋กœ, modCount์ด๋‹ค! ํ•ด๋‹น ๋ฆฌ์ŠคํŠธ์— ๋Œ€ํ•ด ์ˆ˜์ •์ด ๊ฐ€ํ•ด์กŒ๊ธฐ ๋•Œ๋ฌธ์— modCount๋ฅผ ์ฆ๊ฐ€ํ•˜๊ฒŒ ๋˜๊ณ , ํ•ด๋‹น ์ธ๋ฑ์Šค ์ดํ›„์˜ ๋ชจ๋“  ์š”์†Œ๋ฅผ ์•ž์œผ๋กœ ํ•œ ์นธ์”ฉ ๋‹น๊ธฐ๋Š” ์ž‘์—…์„ ์ง„ํ–‰ํ•œ๋‹ค.

 

๊ทธ๋Ÿผ, ์—ฌ๊ธฐ์„œ ํ˜„์žฌ modCount๋Š” 1 ์ฆ๊ฐ€ํ•œ ํ˜•ํƒœ์ผ ๊ฒƒ์ด๋‹ค. ์ด ์ƒํƒœ๋กœ ๋‹ค์Œ iterator๋ฅผ ๋Œ๊ฒŒ ๋˜๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ?

์ฝ”๋“œ๋งŒ ๋„ฃ์œผ๋ฉด ์ง€๋ฃจํ• ๊นŒ๋ด ์ด๋ฏธ์ง€... ๊ทผ๋ฐ ๋” ์ง€๋ฃจํ•จ

public boolean hasNext() {
    return cursor != size;
}

๋จผ์ €, hasNext๋ฅผ ํ†ตํ•ด์„œ cursor์™€ ์‚ฌ์ด์ฆˆ๊ฐ€ ์ผ์น˜ํ•˜๋Š”์ง€ ์ฒดํฌํ•ด์„œ ๋๊นŒ์ง€ ๋„๋‹ฌํ–ˆ๋Š”์ง€๋ฅผ ์ฒดํฌํ•œ๋‹ค.

๊ทธ๋ฆฌ๊ณ , ๋‹ค์‹œ next()๋ฅผ ํ†ตํ•ด์„œ ์š”์†Œ๋ฅผ ๊บผ๋‚ด์˜ค๊ฒŒ ๋  ๊ฒƒ์ด๋‹ค. ๊ทธ๋ฆฌ๊ณ , ๋˜ ๋‹ค์‹œ checkForComodification() ๋ฉ”์„œ๋“œ๋ฅผ ํ™•์ธํ•˜๊ฒŒ ๋  ๊ฒƒ์ด๊ณ , ์ด๋•Œ ์ด๋ฏธ ๋ณ€๊ฒฝ๋œ modCount ๋ณ€์ˆ˜๋กœ ์ธํ•ด์„œ expectedModCount์™€ ๋‹ฌ๋ผ์ง€๊ฒŒ ๋˜์–ด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์ด๋‹ค! ๐Ÿฅฒ

์‹ค์ œ๋กœ ๋””๋ฒ„๊น… ๊ฒฐ๊ณผ, ์ดˆ๊ธฐ์˜ expectedModCount๋Š” 3์ด์ง€๋งŒ, modCount๋Š” 4๊ฐ€ ๋˜์–ด์„œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•จ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค!

 


โœ”๏ธ ์—‡... ๊ทธ๋Ÿผ ์š”์†Œ๊ฐ€ 2๊ฐœ์ผ ๋•Œ๋Š” ์™œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์•˜์ง€?

์ด์œ ๋Š” ๋‹จ์ˆœํ•˜๋‹ค. 'hi' ์š”์†Œ๊ฐ€ ์ œ๊ฑฐ๋œ ์ดํ›„, ๋ฆฌ์ŠคํŠธ์™€ iterator์˜ ์ •๋ณด์ด๋‹ค. ์ด๋ฏธ ๋ฆฌ์ŠคํŠธ์—์„œ ์š”์†Œ๊ฐ€ ์ œ๊ฑฐ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋‚ด๋ถ€์ ์œผ๋กœ ๊ด€๋ฆฌ๋˜๋Š” ArrayList์˜ ์‚ฌ์ด์ฆˆ๊ฐ€ 1์ž„์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. (hello๋งŒ ๋‚จ์•„์žˆ์Œ) 

 for (Iterator<Crew> iterator = crews.listIterator(); iterator.hasNext();) {
    Crew crew = iterator.next();
    if (crew.getAge() == 20) {
        crews.remove(crew);
    }
}

์ด๋•Œ, iterator์˜ hasNext()๋ฅผ ํ†ตํ•ด ๋‹ค์Œ ์š”์†Œ๊ฐ€ ์กด์žฌํ•˜๋Š”์ง€ ์ฒดํฌํ•˜๋Š”๋ฐ, ์•„๊นŒ ๋ดค๋˜ ๊ฒƒ์ฒ˜๋Ÿผ cursor์™€ size์˜ ์ •๋ณด๊ฐ€ ๋™์ผํ•˜์ง€ ์•Š๋‹ค๋ฉด ๋‹ค์Œ ์š”์†Œ๊ฐ€ ์žˆ๋‹ค๊ณ  ํŒ๋‹จํ•œ๋‹ค. ์ด๋•Œ, 1 (์ปค์„œ ์œ„์น˜) = 1 (์‚ฌ์ด์ฆˆ)๋กœ ๋‘ ๊ฐœ๊ฐ€ ๋™์ผํ•œ ์ƒํ™ฉ์ด ๋งŒ๋“ค์–ด์ง€๋ฉด์„œ, next() ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ๊ฒ€์ฆ ๋ฉ”์„œ๋“œ๋ฅผ ๊ฑฐ์น˜๊ธฐ๋„ ์ „์— ๊ทธ๋ƒฅ for๋ฌธ์ด ์ข…๋ฃŒ๋˜์–ด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€๋„ ์•Š๊ณ  ๋๋‚˜๋Š” ๊ฒƒ์ด์—ˆ๋‹ค.

 


โœ”๏ธ ์ •๋ฆฌ - ๊ทธ๋Ÿฌ๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์ง€?

๐Ÿ’ก ๊ทธ๋ž˜์„œ, ์ •ํ™•ํ•˜๊ฒŒ๋Š” '์กฐ๊ฑด์— ํ•ด๋‹นํ•˜๋Š” ์š”์†Œ' ๋‹ค์Œ์˜ ์š”์†Œ๊ฐ€ 1๊ฐœ๋งŒ ์กด์žฌํ•˜๊ณ , ๋ฐ˜๋ณต์ž๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ์ปค์„œ ์œ„์น˜์™€ ์‚ฌ์ด์ฆˆ๊ฐ€ ๋™์ผํ•œ ์ƒํ™ฉ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๊ณ  ์ข…๋ฃŒ๋  ์ˆ˜ ์žˆ๋‹ค! ๋ผ๊ณ  ์ดํ•ดํ•˜๋ฉด ๋œ๋‹ค.

 // ์˜ˆ์™ธ ๋ฐœ์ƒ X
        List<Crew> crews = new ArrayList<>();
        crews.add(Crew.createByNickname("hello", 21, Course.BACKEND));
        crews.add(Crew.createByNickname("hi", 20, Course.BACKEND));
        crews.add(Crew.createByNickname("hihi", 23, Course.BACKEND));

        for (Iterator<Crew> iterator = crews.listIterator(); iterator.hasNext(); ) {
            Crew crew = iterator.next();
            if (crew.getAge() == 20) {
                crews.remove(crew);
            }
        }

        // ์˜ˆ์™ธ ๋ฐœ์ƒ
        List<Crew> crews = new ArrayList<>();
        crews.add(Crew.createByNickname("hello", 21, Course.BACKEND));
        crews.add(Crew.createByNickname("hi", 20, Course.BACKEND));
        crews.add(Crew.createByNickname("hihi", 23, Course.BACKEND));
        crews.add(Crew.createByNickname("wow", 23, Course.BACKEND));

        for (Iterator<Crew> iterator = crews.listIterator(); iterator.hasNext(); ) {
            Crew crew = iterator.next();
            if (crew.getAge() == 20) {
                crews.remove(crew);
            }
        }

๋‹จ์ˆœํžˆ ์š”์†Œ์˜ ๊ฐœ์ˆ˜๋งŒ ์ƒ๊ด€์žˆ๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์กฐ๊ฑด์— ์ผ์น˜ํ•˜๋Š” ์š”์†Œ ๋‹ค์Œ์˜ ์š”์†Œ๊ฐ€ 2๊ฐœ ์ด์ƒ ์กด์žฌํ•  ๋•Œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

 

๊ทธ๋ž˜์„œ, ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์š”์†Œ ์ž์ฒด๋ฅผ ์‚ญ์ œํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค๋Š” iterator๋ฅผ remove() ํ•ด์ค˜์•ผ ํ•œ๋‹ค.

List<Crew> crews = new ArrayList<>();
crews.add(Crew.createByNickname("hi", 20, Course.BACKEND));
crews.add(Crew.createByNickname("hello", 21, Course.BACKEND));
crews.add(Crew.createByNickname("hihi", 23, Course.BACKEND));

for (Iterator<Crew> iterator = crews.listIterator(); iterator.hasNext();) {
    Crew crew = iterator.next();
    if (crew.getAge() == 20) {
        iterator.remove();
    }
}

 

๊ทธ ์ด์œ ๋Š” ๋‚ด๋ถ€ ๊ตฌํ˜„์„ ๋ณด๋ฉด ๋ฐ”๋กœ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

public void remove() {
    if (lastRet < 0)
        throw new IllegalStateException();
    checkForComodification();

    try {
        ArrayList.this.remove(lastRet);
        cursor = lastRet;
        lastRet = -1;
        expectedModCount = modCount; // ํ•ต์‹ฌ ๋กœ์ง!
    } catch (IndexOutOfBoundsException ex) {
        throw new ConcurrentModificationException();
    }
}

lastRet๋Š” next()์—์„œ ์ฐพ์€ ์š”์†Œ์˜ ์ธ๋ฑ์Šค๋ฅผ ์ €์žฅํ•ด๋‘์—ˆ๋Š”๋ฐ, ํ•ด๋‹น ์ธ๋ฑ์Šค์— ํ•ด๋‹นํ•˜๋Š” ์š”์†Œ๋ฅผ ์ œ๊ฑฐํ•˜๊ณ , ์ปค์„œ๋ฅผ ํ•ด๋‹น ์ธ๋ฑ์Šค๋กœ ๋Œ๋ฆฌ๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ , ์ฐพ์€ ์ธ๋ฑ์Šค์— ๋Œ€ํ•ด์„œ -1๋กœ ์„ค์ •ํ•ด์„œ ์ œ๊ฑฐ ์™„๋ฃŒ๋˜์—ˆ์Œ์„ ํ‘œ์‹œํ•ด๋‘๊ณ , expectedModCount์™€ modCount์˜ ์ƒํƒœ๋ฅผ ์ผ์น˜์‹œ์ผœ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋„๋ก ๋งŒ๋“ค์–ด ์ค€๋‹ค! ๐Ÿ‘

 

 

์ปฌ๋ ‰์…˜ ์ˆœํšŒ ์‹œ, ๋ฌด์‹ฌ์ฝ” ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ์›ฌ๋งŒํ•˜๋ฉด ๋”ฐ๋กœ ๋ณต์‚ฌ๋ณธ์„ ์ฐธ์กฐํ•˜์—ฌ ์ œ๊ฑฐํ•˜๋„๋ก ํ•˜๊ฑฐ๋‚˜, ์ด๋Ÿฐ ์‹์œผ๋กœ iterator๋ฅผ ์ œ๊ฑฐํ•˜๋Š” ์‹์œผ๋กœ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์ž ๐Ÿ˜Ž

Comments