[java] List에서 요소를 제거하려고 할 때 UnsupportedOperationException이 발생하는 이유는 무엇입니까?

이 코드가 있습니다 :

public static String SelectRandomFromTemplate(String template,int count) {
   String[] split = template.split("|");
   List<String> list=Arrays.asList(split);
   Random r = new Random();
   while( list.size() > count ) {
      list.remove(r.nextInt(list.size()));
   }
   return StringUtils.join(list, ", ");
}

나는 이것을 얻는다 :

06-03 15:05:29.614: ERROR/AndroidRuntime(7737): java.lang.UnsupportedOperationException
06-03 15:05:29.614: ERROR/AndroidRuntime(7737):     at java.util.AbstractList.remove(AbstractList.java:645)

이것이 올바른 방법은 무엇입니까? 자바 .15



답변

코드와 관련된 몇 가지 문제 :

Arrays.asList고정 된 크기의 목록을 반환

API에서 :

Arrays.asList: 지정된 배열이 지원 하는 고정 크기 목록을 반환합니다 .

당신은 add그것을 할 수 없습니다 ; 당신은 remove그것에서 할 수 없습니다 . 을 구조적으로 수정할 수 없습니다 List.

고치다

LinkedList더 빨리 지원하는를 만듭니다 remove.

List<String> list = new LinkedList<String>(Arrays.asList(split));

split정규식을 복용

API에서 :

String.split(String regex): 주어진 정규 표현식 과 일치하는 문자열을 분할합니다 .

|정규식 메타 문자입니다. 리터럴로 분할 |하려면 \|Java 문자열 리터럴 인 이스케이프로 이스케이프해야합니다 "\\|".

고치다:

template.split("\\|")

더 나은 알고리즘

remove임의의 인덱스로 한 번에 하나씩 호출하는 대신 범위에서 충분한 임의의 숫자를 생성 한 다음 적절한 인덱스를 호출 하여을 사용하여 List한 번 순회하는 것이 좋습니다. 주어진 범위에서 임의의 고유 한 숫자를 생성하는 방법에 대한 질문이 있습니다.listIterator()remove()

이를 통해 알고리즘은입니다 O(N).


답변

이건 여러 번 나를 태워 버렸습니다. Arrays.asList수정할 수없는 목록을 만듭니다. Javadoc에서 : 지정된 배열이 지원 하는 고정 크기 목록을 리턴합니다 .

동일한 내용으로 새 목록을 만듭니다.

newList.addAll(Arrays.asList(newArray));

이렇게하면 약간의 추가 가비지가 생성되지만이를 변경할 수 있습니다.


답변

수정 불가능한 래퍼로 작업하고 있기 때문일 수 있습니다 .

이 줄을 바꾸십시오 :

List<String> list = Arrays.asList(split);

이 줄에 :

List<String> list = new LinkedList<>(Arrays.asList(split));


답변

나는 그것을 대체한다고 생각한다.

List<String> list = Arrays.asList(split);

List<String> list = new ArrayList<String>(Arrays.asList(split));

문제를 해결합니다.


답변

에 의해 반환 된 목록은 Arrays.asList()변경 불가능할 수 있습니다. 당신이 시도 할 수 있습니다

List<String> list = new ArrayList(Arrays.asList(split));


답변

asList 메소드에 대한 JavaDoc을 읽으십시오.

지정된 배열에있는 객체의 {@code List}를 반환합니다. {@code List}의 크기는 수정할 수 없습니다. 즉 추가 및 제거는 지원되지 않지만 요소는 설정할 수 있습니다. 요소를 설정하면 기본 배열이 수정됩니다.

이것은 Java 6에서 왔지만 안드로이드 java와 동일합니다.

편집하다

결과 목록의 유형은 Arrays.ArrayListArrays.class 내부의 개인 클래스입니다. 실제로 말하자면, 전달한 배열의 List-view에 지나지 않습니다 Arrays.asList. 결과적으로 배열을 변경하면 목록도 변경됩니다. 또한 배열의 크기를 조정할 수 없으므로 제거 및 추가 작업 이 지원되지 않아야 합니다.


답변

Arrays.asList ()는 크기에 영향을주는 작업을 허용하지 않는 목록을 반환합니다 ( “수정 불가능”과 동일하지 않음).

new ArrayList<String>(Arrays.asList(split));실제 사본을 만들 수는 있지만 수행하려는 작업을 확인하는 경우 추가 제안 사항이 있습니다 ( O(n^2)알파벳 바로 아래에 알고리즘이 있음).

목록에서 임의의 요소 를 제거 list.size() - count(이것이라고 함 k) 하려고 합니다. 임의의 요소를 여러 개 선택 k하여 목록 의 끝 위치로 바꾸고 전체 범위를 삭제하십시오 (예 : subList () 및 clear () 사용). 그것은 그것을 희박하고 평균적인 O(n)알고리즘으로 바꿀 것 O(k)입니다 (보다 정확합니다).

업데이트 : 아래에 언급 된 것처럼이 알고리즘은 요소가 정렬되지 않은 경우 (예 : List가 Bag을 나타내는 경우에만) 의미가 있습니다. 반면에 목록에 의미있는 순서가있는 경우이 알고리즘은이를 유지하지 않습니다 (대신 다유 윤활제 알고리즘).

업데이트 2 : 따라서 회고 적으로 더 나은 (선형, 유지 순서, 그러나 임의의 O (n) 난수) 알고리즘은 다음과 같습니다.

LinkedList<String> elements = ...; //to avoid the slow ArrayList.remove()
int k = elements.size() - count; //elements to select/delete
int remaining = elements.size(); //elements remaining to be iterated
for (Iterator i = elements.iterator(); k > 0 && i.hasNext(); remaining--) {
  i.next();
  if (random.nextInt(remaining) < k) {
     //or (random.nextDouble() < (double)k/remaining)
     i.remove();
     k--;
  }
}