2016-09-05 1 views
1

그래서 나는 다음과 같은 런타임 오류가 점점 오전 : 이제해, ConcurrentModificationException

Exception in thread "main" java.util.ConcurrentModificationException 
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901) 
at java.util.ArrayList$Itr.next(ArrayList.java:851) 
at Solution.getAnswers(Solution.java:53) 
at Solution.getAnswers(Solution.java:44) 
at Solution.getAnswers(Solution.java:44) 
at Solution.getEquations(Solution.java:28) 
at Solution.main(Solution.java:22)  

나는 예외에 읽어하고 내 지식 내가 실행하지 않기 때문에, 무슨 일이 일어나고 이유에로 혼란 스러워요을 모든 비동기 코드. 나는 배열리스트가 쓰레드에 안전하지 않다는 것을 안다.하지만 목록에 값을 추가하는 것이 왜 문제가되는지 확신 할 수 없다. 또한 재귀를 사용하여 동시성 문제가 발생할 것이라고 생각하지 않았습니다.

문제에 대해 어느 정도 밝혀 낼 수 있다면 왜 그런 일이 일어나고 있는지 많은 의견을 듣겠습니다! 내 코드는 아래에 있으며 코드의 끝에있는 재귀 메서드에서 오류가 발생한다고 생각합니다.

public class Solution { 

    public static void main(String[] args) { 
     Scanner scan = new Scanner(System.in); 
     int a = scan.nextInt(); 
     int b = scan.nextInt(); 
     long result = getEquations(a,b); 
     System.out.println(result); 
    } 

    private static long getEquations(int a, int b) { 
     ArrayList<Integer> answers = new ArrayList<Integer>(); 
     getAnswers(a,b,answers); 
     return answers.stream().mapToInt(x->x).distinct().count(); 
    } 

    private static void getAnswers(int a, int b, ArrayList<Integer> answers){ 
     if(a==0 && b==0) return; 
     if(a==1 && b == 0) { 
      answers.add(1); 
      return; 
     } 
     if(a==0 && b == 1) { 
      answers.add(2); 
      return; 
     } 
     if(a>0){ 
      a-=1; 
      getAnswers(a,b,answers); 
      for(Integer value : answers){ 
       answers.add(1+value); 
       answers.add(1*value); 
      } 
      return; 
     } 
     if(b>0){ 
      b-=1; 
      getAnswers(a,b,answers); 
      for(Integer value : answers){ 
       answers.add(2+value); 
       answers.add(2*value); 
      } 
      return; 
     } 
    } 
} 

답변

7

는 읽기 documentation : 당신이 반복하는 동안 목록을 수정하고 있기 때문에 귀하의 코드는이 예외를 던지고있다

Note that this exception does not always indicate that an object has been concurrently modified by a different thread. If a single thread issues a sequence of method invocations that violates the contract of an object, the object may throw this exception. For example, if a thread modifies a collection directly while it is iterating over the collection with a fail-fast iterator, the iterator will throw this exception.

. ListIterator을 사용하면 반복하는 동안 (현재 위치에) 안전하게 삽입 할 수 있습니다.

1

루프에서 반복하는 동안 컬렉션을 수정합니다. 그건 허용되지 않습니다.

for(Integer value : answers) { /* Iterator is created implicitly here */ 
    answers.add(1+value);  /* Underlying collection is modified here */ 
    ... 

허용되는 유일한 변경은 Iteratorremove() 인스턴스를 호출한다.

2

@shmosel의 대답은 일반적으로 좋지만, 항목의 순서가 코드에 중요하면 작동하지 않습니다. 이 중요한 경우에는이를 대체 할 수있는이와

 for(Integer value : answers){ 
      answers.add(1+value); 
      answers.add(1*value); 
     } 

을 :

 ArrayList<Integer> toAdd=new ArrayList<Integer>(); 
     for(Integer value : answers){ 
      toAdd.add(1+value); 
      toAdd.add(1*value); 
     } 
     answers.addAll(toAdd); 

이 새로운 코드는 여전히 같은 순서로 답변을 생성합니다.

getEquations에서만 getAnswers를 호출하는 경우 getEquations가 arraylist의 항목 순서와 관계가 없기 때문에 문제가되지 않지만 언젠가 있을지 여부에 따라 고려할 수 있습니다 다른 코드. @shmosel 이미 내가 좀 더 문제를 설명하려고합니다 공식적인 답변을 주신 동안

1

는 :

이 값 {1, 2, 3, 4, 5, 6} 크기 목록의List<Integer> list에 대해 생각을 분명히 6입니다.이제

대신 자바는 우리가 루프 좋은 오래된을 사용하여 제공하는 반복자 마법 사용 :
i -> 0, list.size() -> 6
i -> 1, list.size() -> 7
i -> 2, list.size() -> 8
:

여기
for(int i = 0; i < list.size(); i++) { 
    int someValue = doSomeMath(); 
    list.add(someValue); 
} 

는 반복의 모습 방법입니다 i -> 3, list.size() -> 9

e에 목록에 값을 추가 할 것이므로 매우 반복적 일 때 우리는 무한 루프에 갇혀있을 것입니다. 왜냐하면 우리가 반복하는 것과 같은 비율로리스트가 커지기 때문에 결코 i == list.size()에 도달하지 않을 것이기 때문입니다.

이 문제를 방지하기 위해 반복자가 반복하는 동안 값을 추가하면 완료 할 수 없으므로 반복자 (for(Integer value : list) 루프)가 예외를 throw합니다.

관련 문제