Collection에서 add나 remove를 할 때, loop 안에서 작업하면 CME 가 발생할 수 있다.


@Test
public void exceptionTest() {
List<Member> members = Lists.newArrayList();
members.add(new Member("1", "true"));
members.add(null);
members.add(new Member("3", "false"));

for (Member member : members) {
if (member == null) {
members.remove(member);
members.add(new Member("4", "true")); // 여기서 CME 발생
}
}

System.out.println(members.size());
}

결과

java.util.ConcurrentModificationException

at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)

at java.util.ArrayList$Itr.next(ArrayList.java:851)

at com.google.NullElementTest.exceptionTest(NullElementTest.java:20)







Collection에서 null 인 엘리먼트를 삭제하려면, Collections.removeAll()을 사용할 수 있다.

바로 Collections.removeAll(null)을 호출하면, cme가 발생한다.


따라서, 이를 해결하려면, Collection을 하나 만들고 null 엘리먼트를 추가한 후, members.removeAll(리스트)를 호출한다.

@Test
public void avoidNpeTest() {
List<Member> members = Lists.newArrayList();
members.add(new Member("1", "true"));
members.add(null);
members.add(new Member("3", "false"));

// members.removeAll(null); // error

List<Member> nullList = Lists.newArrayList();
nullList.add(null);
members.removeAll(nullList);

System.out.println(members);
}


결과


[com.google.NullElementTest$Member@2133c8f8[id=1,followable=true], com.google.NullElementTest$Member@3ac3fd8b[id=3,followable=false]]




조금 verbose하니, Collections.singleton(null)을 이용하면 이전 예시와 동일한 결과를 얻을 수 있다.


@Test
public void avoidElegantNpeTest() {
List<Member> members = Lists.newArrayList();
members.add(new Member("1", "true"));
members.add(null);
members.add(new Member("3", "false"));

System.out.println(Collections.singleton(null));

members.removeAll(Collections.singleton(null));
System.out.println(members);
}


결과


[null]

[com.google.NullElementTest$Member@2133c8f8[id=1,followable=true], com.google.NullElementTest$Member@3ac3fd8b[id=3,followable=false]]








참고 : 코드 전문


package com.google;

import com.clearspring.analytics.util.Lists;
import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.StringUtils;
import org.junit.Test;

import java.util.Collections;
import java.util.List;

public class CMETest {

@Test
public void exceptionTest() {
List<Member> members = Lists.newArrayList();
members.add(new Member("1", "true"));
members.add(null);
members.add(new Member("3", "false"));

for (Member member : members) {
if (member == null) {
members.remove(member);
members.add(new Member("4", "true"));
}
}

System.out.println(members.size());
}

@Test
public void avoidNpeTest() {
List<Member> members = Lists.newArrayList();
members.add(new Member("1", "true"));
members.add(null);
members.add(new Member("3", "false"));

// members.removeAll(null); // error

List<Member> nullList = Lists.newArrayList();
nullList.add(null);
members.removeAll(nullList);

System.out.println(members);
}

@Test
public void avoidElegantNpeTest() {
List<Member> members = Lists.newArrayList();
members.add(new Member("1", "true"));
members.add(null);
members.add(new Member("3", "false"));

System.out.println(Collections.singleton(null));

members.removeAll(Collections.singleton(null));
System.out.println(members);
}

class Member {
public String id;
public String followable;

public Member(String id, String f) {
this.id = id;
this.followable = f;
}

public String toString() {
return ReflectionToStringBuilder.toString(this);
}
}
}


Posted by '김용환'
,