변경 불가능한 컬렉션과 수정 불가능한 컬렉션


170

보내는 사람 컬렉션 프레임 워크 개요 :

수정 작업을 지원하지 않는 컬렉션은 (예컨대 add, removeclear)라고도 불가능한 . 수정할 수 없는 컬렉션은 수정할 수 있습니다.

Collection객체의 변화가 보이지 않도록 추가로 보장하는 컬렉션은 불변 이라고합니다 . 변경할 수없는 컬렉션은 변경 가능 합니다.

차이를 이해할 수 없습니다. 여기서 수정 불가능불변
의 차이점은 무엇입니까 ?

답변:


207

수정 불가능한 콜렉션은 종종 다른 코드가 여전히 액세스 할 수 있는 수정 가능한 콜렉션의 랩퍼 입니다. 그래서 당신은 당신 만 변경 불가능한 컬렉션에 대한 참조가있는 경우이를 변경할 수 없습니다, 당신은 변경하지 않는 내용에 의존 할 수 없다.

불변 수집 보장하는 것이 아무것도 더 이상 컬렉션을 변경할 수 있습니다. 수정 가능한 컬렉션을 래핑하는 경우 다른 코드가 해당 수정 가능한 컬렉션에 액세스 할 수 없도록합니다. 코드가 컬렉션에 참조를 포함하는 개체를 변경할 수는 없지만 개체 자체는 여전히 변경 가능할 수 있습니다.StringBuilder .

기본적으로 차이점은 다른 코드가 컬렉션 뒤에서 컬렉션을 변경할 수 있는지에 대한 것입니다.


63
불변 컬렉션은 더 이상 아무것도 변하지 않는다고 보장하지 않습니다 . 그것은 단지 컬렉션 자체가 (포장이 아니라 복사에 의해) 변경 될 수 없도록합니다. 컬렉션에 존재하는 개체는 여전히 변경 될 수 있으며 해당 개체에 대한 보장은 없습니다.
Hiery Nomus

8
@HieryNomus : 아무 것도 바꿀 수 없다고 말하지 않았다는 점에 주목하십시오-나는 컬렉션을 바꿀 수 없다고 말했습니다.
Jon Skeet

1
좋아, 그것을 잘못 읽었을 수도있다;) 그러나 그것을 명확히하는 것이 좋다.
Hiery Nomus

5
그래서, 당신이 말하는 것. 진정한 불변성을 위해서는 불변 유형의 항목을 포함하는 불변 컬렉션이 필요합니다.
Evan Plaice

1
@savanibharat : 여전히 수정할 수있는 코드 경로가 있는지 여부에 따라 다릅니다 list. 나중에 전화 list.add(10)를 걸 수 있다면 coll그 변화를 반영 할 것이므로 아니오, 불변이라고 부르지 않을 것입니다.
Jon Skeet

92

기본적으로 unModifiable컬렉션은 뷰이므로 간접적으로 수정 가능한 다른 참조에서 여전히 '수정'될 수 있습니다. 또한 읽기 전용보기로 콜렉션 로서 소스 콜렉션이 변경 될 때 수정 불가능한 콜렉션은 항상 최신 값으로 표시됩니다.

그러나 immutable콜렉션 은 다른 콜렉션 의 읽기 전용 사본 으로 취급 될 수 있으며 수정할 수 없습니다. 이 경우 소스 콜렉션이 변경되면 변경 불가능한 콜렉션은 변경 사항을 반영하지 않습니다.

다음은 이러한 차이를 시각화하는 테스트 사례입니다.

@Test
public void testList() {

    List<String> modifiableList = new ArrayList<String>();
    modifiableList.add("a");

    System.out.println("modifiableList:"+modifiableList);
    System.out.println("--");


    //unModifiableList

    assertEquals(1, modifiableList.size());

    List<String> unModifiableList=Collections.unmodifiableList(
                                        modifiableList);

    modifiableList.add("b");

    boolean exceptionThrown=false;
    try {
        unModifiableList.add("b");
        fail("add supported for unModifiableList!!");
    } catch (UnsupportedOperationException e) {
        exceptionThrown=true;
        System.out.println("unModifiableList.add() not supported");
    }
    assertTrue(exceptionThrown);

    System.out.println("modifiableList:"+modifiableList);
    System.out.println("unModifiableList:"+unModifiableList);

    assertEquals(2, modifiableList.size());
    assertEquals(2, unModifiableList.size());
            System.out.println("--");



            //immutableList


    List<String> immutableList=Collections.unmodifiableList(
                            new ArrayList<String>(modifiableList));

    modifiableList.add("c");

    exceptionThrown=false;
    try {
        immutableList.add("c");
        fail("add supported for immutableList!!");
    } catch (UnsupportedOperationException e) {
        exceptionThrown=true;
        System.out.println("immutableList.add() not supported");
    }
    assertTrue(exceptionThrown);


    System.out.println("modifiableList:"+modifiableList);
    System.out.println("unModifiableList:"+unModifiableList);
    System.out.println("immutableList:"+immutableList);
    System.out.println("--");

    assertEquals(3, modifiableList.size());
    assertEquals(3, unModifiableList.size());
    assertEquals(2, immutableList.size());

}

산출

modifiableList:[a]
--
unModifiableList.add() not supported
modifiableList:[a, b]
unModifiableList:[a, b]
--
immutableList.add() not supported
modifiableList:[a, b, c]
unModifiableList:[a, b, c]
immutableList:[a, b]
--

차이가 보이지 않습니다. 불변이 어떻게 다른지 지적 해 주시겠습니까? Immutable과 unmodifiable에 오류가 발생하고 add가 지원되지 않는 것을 볼 수 있습니다. 여기에 뭔가 빠졌습니까?
AKS

2
@AKS 목록에 'C'를 첨가 한 후 마지막 세 목록 항목의 출력을 참조하십시오 모두 크기는 동안 modifiableListunModifiableList증가 immutableList크기가 변경되지 않았습니다
Prashant Bhate을

1
오! 알았다! :) .. 여기서 modifiableList의 변경 사항을 사용하여 unmodifableList를 수정했지만 ImmutableList는 수정할 수 없습니다. 그러나 ImmutableList를 수정할 수있는 것과 같은 방법으로 클라이언트가 ImmutableList 참조에만 액세스 할 수 있다고 생각합니다 .ImmutableList가 생성 된 modifiableList에 대한 참조는 클라이언트에 노출되지 않습니다. 권리?
AKS

1
new ArrayList<String>(modifiableList)immutableList에 대한 참조가 없기 때문에 가능합니다
Prashant Bhate

@PrashantBhate 안녕하세요, new ArrayList<String>(modifiableList)때문에 참조가 없습니다 new? 감사.
Unheilig

11

주요 차이점은 변경 가능한 컬렉션의 소유자가 다른 코드에 컬렉션에 대한 액세스를 제공하려고하지만 다른 코드가 컬렉션을 수정하지 못하게하는 인터페이스를 통해 액세스를 제공한다는 것입니다 (그 기능을 예약하는 동안) 소유 코드). 따라서 컬렉션은 변경할 수 없지만 특정 사용자는 컬렉션을 변경할 수 없습니다.

오라클의 Java Collection Wrapper 튜토리얼 은 다음과 같이 말합니다 (강조 추가).

수정 불가능한 랩퍼는 다음과 같이 두 가지 주요 용도로 사용됩니다.

  • 컬렉션을 만든 후에는 컬렉션을 변경할 수 없습니다. 이 경우 백업 모음에 대한 참조를 유지하지 않는 것이 좋습니다. 이것은 절대적으로 불변성을 보장합니다.
  • 특정 클라이언트가 데이터 구조에 대한 읽기 전용 액세스를 허용합니다. 백업 컬렉션에 대한 참조는 유지하지만 래퍼에 대한 참조는 제공합니다. 이러한 방식으로 클라이언트는 전체 액세스 권한을 유지하면서 보이지만 수정할 수는 없습니다 .

3

만약 우리가 JDK Unmodifiable*와 구아바 에 대해 이야기한다면 Immutable*, 실제로 그 차이도 성능에 있습니다. 변경 불가능한 콜렉션은 일반 콜렉션 주위에 랩퍼 가 아닌 경우 더 빠르고 메모리 효율적일 수 있습니다 (JDK 구현은 랩퍼입니다). 구아바 팀 인용 :

JDK는 Collections.unmodifiableXXX 메소드를 제공하지만 우리의 의견으로는 다음과 같습니다.

<...>

  • 비효율적 : 데이터 구조에는 동시 수정 검사, 해시 테이블의 추가 공간 등을 포함하여 변경 가능한 컬렉션의 모든 오버 헤드가 여전히 있습니다.

성능을 생각할 때 수정 불가능한 래퍼가 컬렉션을 복사하지 않는다는 점을 고려해야합니다. 구아바와 현재 jdk9 +에서 사용되는 불변 버전은 예를 들어 List.of(...)실제로 두 번 복사합니다!
benez

2

Java ™ 자습서 를 인용하려면 다음을 수행하십시오.

랩핑 된 콜렉션에 기능을 추가하는 동기화 랩퍼와 달리 수정 불가능한 랩퍼는 기능을 제거합니다. 특히 컬렉션을 수정하는 모든 작업을 가로 채서 UnsupportedOperationException을 발생시켜 컬렉션을 수정하는 기능을 제거합니다 . 수정 불가능한 랩퍼는 다음과 같이 두 가지 주요 용도로 사용됩니다.

  • 컬렉션을 만든 후에는 컬렉션을 변경할 수 없습니다. 이 경우 백업 모음에 대한 참조를 유지하지 않는 것이 좋습니다. 이것은 절대적으로 불변성을 보장합니다.

  • 특정 클라이언트가 데이터 구조에 대한 읽기 전용 액세스를 허용합니다. 백업 컬렉션에 대한 참조는 유지하지만 래퍼에 대한 참조는 제공합니다. 이러한 방식으로 클라이언트는 전체 액세스 권한을 유지하면서 보이지만 수정할 수는 없습니다.

(강조 광산)

이것은 실제로 요약합니다.


1

위에서 언급했듯이 수정 불가능한 콜렉션은 예를 들어 수정 불가능한 콜렉션에 다른 오브젝트가 참조하고 해당 오브젝트가 변경하는 기본 대리자 콜렉션이있는 경우 수정 불가능한 콜렉션이 변경 될 수 있기 때문에 변경 불가능합니다.

불변에 관해서는 잘 정의되어 있지 않습니다. 그러나 일반적으로 개체가 "변경되지 않음"을 의미하지만 재귀 적으로 정의해야합니다. 예를 들어 인스턴스 변수가 모두 프리미티브이고 메서드에 인수가없고 프리미티브를 반환하는 클래스에 대해서는 불변을 정의 할 수 있습니다. 그런 다음 메소드는 재귀 적으로 인스턴스 변수를 변경할 수 없으며 모든 메소드가 변경 불가능하고 불변 값을 리턴하는 인수를 포함 할 수 있습니다. 메소드는 시간이 지남에 따라 동일한 값을 리턴하도록 보장되어야합니다.

우리가 그렇게 할 수 있다고 가정하면 스레드 안전 개념도 있습니다. 그리고 불변 (또는 시간이 지남에 따라 변경되지 않음)도 스레드 안전을 의미한다고 믿게 될 수 있습니다. 그러나 그것은 사실이 아닙니다이것이 다른 답변에서 아직 언급되지 않은 주요 포인트입니다. 항상 동일한 결과를 반환하지만 스레드로부터 안전하지 않은 불변 개체를 만들 수 있습니다. 이를 확인하기 위해 시간이 지남에 따라 추가 및 삭제를 유지하여 불변 콜렉션을 구성한다고 가정합니다. 이제 불변의 컬렉션은 내부 컬렉션 (시간이 지남에 따라 변경 될 수 있음)을보고 컬렉션을 만든 후 추가되거나 삭제 된 요소를 내부적으로 추가 및 삭제하여 해당 요소를 반환합니다. 컬렉션은 항상 동일한 요소를 반환하지만 분명히 값을 변경하지 않기 때문에 스레드로부터 안전하지 않습니다.

이제 우리는 불변을 스레드로부터 안전하고 절대 변경되지 않는 객체로 정의 할 수 있습니다. 일반적으로 이러한 클래스로 이어지는 불변 클래스를 작성하기위한 지침이 있지만, 예를 들어, 위의 "스냅 샷"콜렉션 예제에 설명 된대로 스레드 안전성에주의를 기울여야하는 불변 클래스를 작성하는 방법이있을 수 있습니다.


1

Java ™ 학습서는 다음과 같이 말합니다.

랩핑 된 콜렉션에 기능을 추가하는 동기화 랩퍼와 달리 수정 불가능한 랩퍼는 기능을 제거합니다. 특히 컬렉션을 수정하는 모든 작업을 가로 채서 UnsupportedOperationException을 발생시켜 컬렉션을 수정하는 기능을 제거합니다. 수정 불가능한 랩퍼는 다음과 같이 두 가지 주요 용도로 사용됩니다.

컬렉션을 만든 후에는 컬렉션을 변경할 수 없습니다. 이 경우 백업 모음에 대한 참조를 유지하지 않는 것이 좋습니다. 이것은 절대적으로 불변성을 보장합니다.

특정 클라이언트가 데이터 구조에 대한 읽기 전용 액세스를 허용합니다. 백업 컬렉션에 대한 참조는 유지하지만 래퍼에 대한 참조는 제공합니다. 이러한 방식으로 클라이언트는 전체 액세스 권한을 유지하면서 보이지만 수정할 수는 없습니다.

차이점을 이해하기에 충분한 설명이 필요하다고 생각합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.