Java의 불변 배열


158

Java의 기본 배열에 대한 불변의 대안이 있습니까? 기본 배열을 만드는 final것이 실제로 다음과 같은 일을 방해하지는 않습니다.

final int[] array = new int[] {0, 1, 2, 3};
array[0] = 42;

배열의 요소를 변경할 수 없도록하고 싶습니다.


43
1) io 2) 헤비 수 크 런칭 3) 자신의 List / Collection을 구현 해야하는 경우 ( 드문 경우) 이외의 것을 위해 Java에서 배열을 사용하지 마십시오 . 이 질문으로 방금 발견했듯이 매우 유연하고 구식입니다.
whaley

39
@Whaley, 성능 및 "동적"배열이 필요없는 코드. 배열은 많은 곳에서 여전히 유용하지만 그렇게 드물지는 않습니다.
콜린 헤 버트

10
@Colin : 그렇습니다. 그러나 그들은 심각하게 구속하고 있습니다; "정말 여기에 배열이 필요합니까? 아니면 대신 List를 사용할 수 있습니까?"
Jason S

7
@Colin : 필요할 때만 최적화하십시오. 예를 들어 배열을 확대하거나 for-loop의 범위를 벗어나는 인덱스를 유지하는 것과 같은 것을 추가하는 데 30 분을 소비하면 이미 상사의 시간을 낭비한 것입니다. 먼저 빌드하고 필요한시기와 장소를 최적화하십시오. 대부분의 응용 프로그램에서는 List를 배열로 바꾸지 않습니다.
fwielstra

9
그럼, 왜 아무도 언급되지 int[]new int[]보다 입력 훨씬 쉽게 List<Integer>new ArrayList<Integer>? XD
lcn

답변:


164

기본 배열에는 없습니다. List 또는 다른 데이터 구조를 사용해야합니다.

List<Integer> items = Collections.unmodifiableList(Arrays.asList(0,1,2,3));

18
어떻게 든 Arrays.asList (T ...)에 대해 알지 못했습니다. 내 ListUtils.list (T ...)를 제거 할 수 있다고 생각합니다.
MattRS

3
그런데, Arrays.asList은 변경 불가능한리스트를 제공합니다
mauhiz

3
@tony의 ArrayList는의 java.util의 아니라고
mauhiz

11
@mauhiz Arrays.asList는 수정할 수 없습니다 . docs.oracle.com/javase/7/docs/api/java/util/… "지정된 배열이 지원하는 고정 크기 목록을 반환합니다. 반환 된 목록이"쓰기 "목록으로 변경됩니다."
Jason S

7
@JasonS는 Arrays.asList()실제로 할 수 없다는 의미에서 수정할 수없는 것처럼 보입니다.add 또는 remove반환의 항목 java.util.Arrays.ArrayList( 혼동되지 않기java.util.ArrayList) -이 작업이 바로 실행되지 않습니다. 어쩌면 @mauhiz가 이것을 말하려고합니까? 그러나 물론 당신은 기존 요소를 수정할 수 List<> aslist = Arrays.asList(...); aslist.set(index, element)있으므로 java.util.Arrays.ArrayList확실히 수정할 수 는 없습니다 . QED 결과 asList와 정상 의 차이를 강조하기 위해 주석을 추가했습니다ArrayList
NIA

73

내 권장 사항은 배열이나 unmodifiableList를 사용하지 않고이 목적을 위해 존재 하는 GuavaImmutableList 를 사용 하는 것입니다.

ImmutableList<Integer> values = ImmutableList.of(0, 1, 2, 3);

3
+1 구아바의 ImmutableList는 Collections.unmodifiableList보다 낫습니다. 왜냐하면 별도의 타입이기 때문입니다.
sleske

29
ImmutableList는 불변이기 때문에 종종 더 좋습니다 (사용 사례에 따라 다름). Collections.unmodifiableList는 변경할 수 없습니다. 오히려 수신기는 변경할 수 없지만 원래 소스는 변경할 수 있다는 견해입니다.
Charlie Collins

2
@CharlieCollins, 원래 소스에 액세스 할 수있는 방법이 없다면 Collections.unmodifiableListList를 변경할 수없는 것입니까?
savanibharat

1
@savanibharat 예.
단지 학생

20

다른 사람들이 지적했듯이 Java에서는 불변 배열을 가질 수 없습니다.

원래 배열에 영향을 미치지 않는 배열을 반환하는 메서드가 절대적으로 필요한 경우 매번 배열을 복제해야합니다.

public int[] getFooArray() {
  return fooArray == null ? null : fooArray.clone();
}

분명히 게터를 호출 할 때마다 전체 사본을 만들므로 다소 비싸지 만 인터페이스를 변경할 수없는 경우 ( List 예를 들어 사용) 클라이언트가 내부를 변경할 위험이 없다면 필요할 수 있습니다.

이 기술을 방어 복사본 만들기라고합니다.


3
그는 게터가 필요하다고 언급 한 곳은 어디입니까?
Erick Robertson

6
@ 에릭 : 그는하지 않았지만 불변의 데이터 구조에 대한 매우 일반적인 사용 사례입니다 (게터에서 더 흔하더라도 솔루션이 모든 곳에서 적용되므로 일반적으로 메소드를 참조하도록 답변을 수정했습니다).
Joachim Sauer

7
clone()또는 Arrays.copy()여기 에서 사용하는 것이 더 낫 습니까?
kevinarpe

내가 기억하는 한 Joshua Bloch의 "Effective Java"에 따르면 clone ()이 확실히 선호되는 방법입니다.
Vincent

1
항목 13의 마지막 문장, "복제 적으로 복제 무시": "복사 기능은 일반적으로 생성자 또는 팩토리에서 제공하는 것이 가장 좋습니다.이 규칙의 주목할만한 예외는 배열이며, 복제 방법으로 가장 잘 복사됩니다."
Vincent

14

Java에서 불변 배열을 만드는 한 가지 방법이 있습니다.

final String[] IMMUTABLE = new String[0];

요소가 0 인 배열은 분명히 변경할 수 없습니다.

List.toArray방법을 사용하여 a List를 배열 로 변환하는 경우 실제로 유용 할 수 있습니다 . 빈 배열이라도 일부 메모리를 차지하므로 상수 빈 배열을 만들어 항상 toArray메서드에 전달하여 해당 메모리 할당을 저장할 수 있습니다 . 전달하는 배열에 충분한 공간이없는 경우 해당 메소드는 새 배열을 할당하지만, 그렇지 않은 경우 (목록이 비어있는 경우) 전달한 배열을 반환하여 호출 할 때마다 해당 배열을 재사용 할 수 있습니다 toArray. 비어 List있습니다.

final static String[] EMPTY_STRING_ARRAY = new String[0];

List<String> emptyList = new ArrayList<String>();
return emptyList.toArray(EMPTY_STRING_ARRAY); // returns EMPTY_STRING_ARRAY

3
그러나 이것은 OP가 데이터를 가진 불변 배열을 달성하는 데 도움이되지 않습니다.
Sridhar Sarnobat

1
@ Sridhar-Sarnobat 그것은 대답의 요점입니다. Java에는 데이터가있는 불변 배열을 만들 수있는 방법이 없습니다.
Brigham

1
이 간단한 구문이 더 좋습니다. private static final String [] EMPTY_STRING_ARRAY = {}; (분명히 "미니 마크 다운"을하는 방법을 찾지 못했습니다. 미리보기 버튼이 좋을 것입니다.)
Wes

6

또 다른 대답

static class ImmutableArray<T> {
    private final T[] array;

    private ImmutableArray(T[] a){
        array = Arrays.copyOf(a, a.length);
    }

    public static <T> ImmutableArray<T> from(T[] a){
        return new ImmutableArray<T>(a);
    }

    public T get(int index){
        return array[index];
    }
}

{
    final ImmutableArray<String> sample = ImmutableArray.from(new String[]{"a", "b", "c"});
}

setter 메소드를 제공하지 않았으므로 생성자에서 해당 배열을 복사하는 것은 무엇입니까?
vijaya kumar

입력 배열의 내용은 여전히 ​​나중에 수정할 수 있습니다. 우리는 원래 상태를 유지하고 싶기 때문에 복사합니다.
aeracode

4

자바 당신이 사용할 수있는 9로 List.of(...), JavaDoc을 .

이 메소드는 변경 불가능 List하며 매우 효율적입니다.


3

'java.lang.Integer'대신 네이티브 'int'가 (성능상의 이유로 또는 메모리를 절약하기 위해) 필요한 경우 자체 래퍼 클래스를 작성해야 할 것입니다. 인터넷에는 다양한 IntArray 구현이 있지만 Koders IntArray , Lucene IntArray 과 같은 변경 불가능한 것은 없습니다 . 아마도 다른 사람들이있을 것입니다.


3

Guava 22부터 패키지에서에 com.google.common.primitives비해 메모리 풋 프린트가 더 작은 3 개의 새로운 클래스를 사용할 수 있습니다 ImmutableList.

또한 빌더가 있습니다. 예:

int size = 2;
ImmutableLongArray longArray = ImmutableLongArray.builder(size)
  .add(1L)
  .add(2L)
  .build();

또는 컴파일 타임에 크기가 알려진 경우 :

ImmutableLongArray longArray = ImmutableLongArray.of(1L, 2L);

이것은 Java 프리미티브에 대한 배열의 불변 뷰를 얻는 또 다른 방법입니다.


1

아니요, 불가능합니다. 그러나 다음과 같이 할 수 있습니다.

List<Integer> temp = new ArrayList<Integer>();
temp.add(Integer.valueOf(0));
temp.add(Integer.valueOf(2));
temp.add(Integer.valueOf(3));
temp.add(Integer.valueOf(4));
List<Integer> immutable = Collections.unmodifiableList(temp);

이것은 래퍼를 사용해야하며 배열이 아닌 List이지만 List는 가장 가깝습니다.


4
모든 것을 작성할 필요가 없습니다 valueOf(). 오토 박싱이이를 처리합니다. 또한 Arrays.asList(0, 2, 3, 4)훨씬 간결 할 것입니다.
Joachim Sauer

@Joachim : 사용 포인트 valueOf()는 내부 Integer 객체 캐시를 활용하여 메모리 소비 / 재활용을 줄이는 것입니다.
Esko

4
@Esko : 오토 박싱 사양을 읽습니다. 정확히 똑같은 일을하므로 여기서 차이가 없습니다.
Joachim Sauer

1
당신은을 변환 NPE 못할거야 @ 존 intInteger생각을; 다른 방법으로 조심해야합니다.
ColinD

1
@ 존 : 당신이 맞아, 위험 할 수 있습니다. 그러나 완전히 피하는 대신 위험을 이해하고 피하는 것이 좋습니다.
Joachim Sauer

1

경우에 따라 Google Guava 라이브러리에서이 정적 메소드를 사용하는 것이 더 가벼워집니다. List<Integer> Ints.asList(int... backingArray)

예 :

  • List<Integer> x1 = Ints.asList(0, 1, 2, 3)
  • List<Integer> x1 = Ints.asList(new int[] { 0, 1, 2, 3})

1

가변성과 복싱을 피하려면 즉시 사용할 수있는 방법이 없습니다. 그러나 기본 배열을 내부에 보유하고 메소드를 통해 요소에 대한 읽기 전용 액세스를 제공하는 클래스를 만들 수 있습니다.


1

Java9of (E ... elements) 메소드는 한 줄만 사용하여 불변 목록을 작성하는 데 사용할 수 있습니다.

List<Integer> items = List.of(1,2,3,4,5);

위의 메소드는 임의의 수의 요소를 포함하는 불변 목록을 리턴합니다. 이 목록에 정수를 추가하면 java.lang.UnsupportedOperationException예외가 발생합니다. 이 메소드는 단일 배열을 인수로 허용합니다.

String[] array = ... ;
List<String[]> list = List.<String[]>of(array);

0

Collections.unmodifiableList()작동 하는 것은 사실이지만 때로는 배열을 반환하도록 정의 된 메소드가있는 큰 라이브러리가있을 수 있습니다 (예 :) String[]. 그것들을 깨뜨리지 않기 위해 실제로 값을 저장할 보조 배열을 정의 할 수 있습니다.

public class Test {
    private final String[] original;
    private final String[] auxiliary;
    /** constructor */
    public Test(String[] _values) {
        original = new String[_values.length];
        // Pre-allocated array.
        auxiliary = new String[_values.length];
        System.arraycopy(_values, 0, original, 0, _values.length);
    }
    /** Get array values. */
    public String[] getValues() {
        // No need to call clone() - we pre-allocated auxiliary.
        System.arraycopy(original, 0, auxiliary, 0, original.length);
        return auxiliary;
    }
}

테스트하려면 :

    Test test = new Test(new String[]{"a", "b", "C"});
    System.out.println(Arrays.asList(test.getValues()));
    String[] values = test.getValues();
    values[0] = "foobar";
    // At this point, "foobar" exist in "auxiliary" but since we are 
    // copying "original" to "auxiliary" for each call, the next line
    // will print the original values "a", "b", "c".
    System.out.println(Arrays.asList(test.getValues()));

완벽하지는 않지만, 적어도 "의사 불변 배열"(클래스 관점에서)이 있으며 이것은 관련 코드를 손상시키지 않습니다.


-3

음 .. 배열은 변형 매개 변수로 상수 (있는 경우)로 전달하는 데 유용합니다.


"변형 변수"란 무엇입니까? 들어 본 적이 없어
sleske

2
배열을 상수로 사용하는 것은 많은 사람들이 실패 하는 곳입니다. 참조는 일정하지만 배열의 내용은 변경할 수 있습니다. 한 명의 발신자 / 고객이 "일정한"내용을 변경할 수 있습니다. 이것은 아마도 당신이 허용하고 싶은 것이 아닙니다. ImmutableList를 사용하십시오.
Charlie Collins

@CharlieCollins 심지어 반사를 통해 해킹 될 수 있습니다. Java는 가변성 측면에서 안전하지 않은 언어이며 변경되지 않습니다.
표시 이름

2
@SargeBorsch 사실이지만, 리플렉션 기반 해킹을하는 사람은 누구나 API 게시자가 액세스하기를 원치 않은 것을 수정하여 불을 피우고 있다는 것을 알아야합니다. 반면에 API가를 반환 int[]하면 호출자는 API 내부에 영향을주지 않고 해당 배열로 원하는 것을 수행 할 수 있다고 가정 할 수 있습니다.
daiscog
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.