우아한 테크코스/테크코스
[Java] UnsupportedOperationException - 배열 asList 관련 에러
jamie.
2020. 2. 17. 21:22
반응형
asList한 Arrays에 add를 하면 해당 에러 발생
UnsupportedOperationException, add를 하고 싶다면 addAll() 또는 생성자를 통해 하도록(아래 코드 참고)
깊은 복사와 얕은 복사깊은 복사 : deep copy : call by value
복사한 원본 또는 복사본 모두 수정이 서로에게 영향을 미치지 않음 - 값을 복사하기 때문
Cloneable을 상속받은 객체를 clone() 메서드로 복사함
얕은 복사 : shallow copy : call by reference
복사한 원본 또는 복사본 모두 수정이 서로에게 영향을 미침 - 주소를 복사하여 서로 참조하기 때문
= 기호로 복사함
List의 덜 깊은 복사?(공식 용어 아님)
List를 대체할 때는 (set API이용) 대체를 해주는데(바꾸는 값)
List를 대체하지 않을 때는(직접 수정, 메서드 등을 통해) 대체가 되지 않고 기존의 주소에서 해당 주소를 참조하는 듯(?)
즉, clone을 해야 완전한 복사인 듯 함!
(나중에 clone 하고 코드 추가해두기)
테스트 코드(학습 테스트)
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class TestException {
@DisplayName("Arrays.asList의 경우, add시 UnsupportedOperationException 발생, 얕은 복사")
@Test
void unsupportedOperationException() {
String string = "가,나,다";
List<String> stringList = Arrays.asList(string.split(","));
assertThatThrownBy(() -> stringList.add("라"))
.isInstanceOf(UnsupportedOperationException.class);
}
@DisplayName("addAll(Arrays.asList())의 경우, (덜) 깊은 복사")
@Test
void success() {
String string = "가,나,다";
List<String> stringList = new ArrayList<>();
stringList.addAll(Arrays.asList(string.split(",")));
stringList.add("라");
assertThat(stringList.get(3)).isEqualTo("라");
}
@DisplayName("new ArrayList<String>(Arrays.asList())의 경우도 (덜) 깊은 복사")
@Test
void successCopy() {
String string = "가,나,다";
List<String> stringList = new ArrayList<>(Arrays.asList(string.split(",")));
stringList.add("라");
assertThat(stringList.get(3)).isEqualTo("라");
}
@DisplayName("얕은/깊은 복사 테스트")
@Test
void copy() {
List<String> stringListBefore = new ArrayList<>();
stringListBefore.add(new String("가"));
stringListBefore.add(new String("나"));
stringListBefore.add(new String("다"));
List<String> stringDeeps1 = new ArrayList<>();
List<String> stringDeeps2 = new ArrayList<>(stringListBefore);
List<String> stringShallow = stringListBefore;
stringDeeps1.addAll(stringListBefore);
stringListBefore.add("라");
// 변경 전 - Reference copy이기 때문에 true
assertThat(stringDeeps1.get(0) == stringListBefore.get(0)).isTrue();
assertThat(stringDeeps2.get(0) == stringListBefore.get(0)).isTrue();
assertThat(stringShallow.get(0) == stringListBefore.get(0)).isTrue();
stringListBefore.set(0, new String("가"));
// 변경 후
assertThat(stringDeeps1.get(0) == stringListBefore.get(0)).isFalse();
assertThat(stringDeeps2.get(0) == stringListBefore.get(0)).isFalse();
assertThat(stringShallow.get(0) == stringListBefore.get(0)).isTrue();
assertThatThrownBy(() -> stringDeeps1.get(3))
.isInstanceOf(IndexOutOfBoundsException.class);
assertThatThrownBy(() -> stringDeeps2.get(3))
.isInstanceOf(IndexOutOfBoundsException.class);
assertThat(stringShallow.get(3)).isEqualTo("라");
stringListBefore.set(0, new String("가나다"));
assertThat(stringDeeps1.get(0)).isEqualTo("가");
assertThat(stringDeeps2.get(0)).isEqualTo("가");
assertThat(stringShallow.get(0)).isEqualTo("가나다");
}
@DisplayName("얕은/깊은 복사 테스트 - 중요!")
@Test
void copy2() {
List<int[]> integerBefore = new ArrayList<>();
integerBefore.add(new int[]{1});
integerBefore.add(new int[]{2});
integerBefore.add(new int[]{3});
List<int[]> integerDeeps1 = new ArrayList<>();
List<int[]> integerDeeps2 = new ArrayList<>(integerBefore);
List<int[]> integerShallow = integerBefore;
integerDeeps1.addAll(integerBefore);
integerBefore.add(new int[]{4});
assertThat(integerDeeps1.get(0)[0] == integerBefore.get(0)[0]).isTrue();
assertThat(integerDeeps2.get(0)[0] == integerBefore.get(0)[0]).isTrue();
assertThat(integerShallow.get(0)[0] == integerBefore.get(0)[0]).isTrue();
// 덜 깊은 복사 ? ? ? ! 진짜 깊은 복사는 clone으로 구현해야!
integerBefore.get(0)[0] = 2;
assertThat(integerDeeps1.get(0)[0] == integerBefore.get(0)[0]).isTrue();
assertThat(integerDeeps2.get(0)[0] == integerBefore.get(0)[0]).isTrue();
assertThat(integerShallow.get(0)[0] == integerBefore.get(0)[0]).isTrue();
}
}
반응형