ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Java] UnsupportedOperationException - 배열 asList 관련 에러
    우아한 테크코스/테크코스 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();
        }
    }
    
    반응형

    댓글

Designed by Tistory.