[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.ArrayList
Arrays.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--;
}
}