배열의 복사본 만들기


345

a지속적으로 업데이트되는 배열 이 있습니다. 말해 보자 a = [1,2,3,4,5]. 정확한 사본을 만들어 a전화해야합니다 b. 경우 a로 변경했다 [6,7,8,9,10], b아직해야한다 [1,2,3,4,5]. 가장 좋은 방법은 무엇입니까? 나는 다음 for과 같은 루프를 시도했다 .

for(int i=0; i<5; i++) {
    b[i]=a[i]
}

그러나 그것은 제대로 작동하지 않는 것 같습니다. 딥 카피 등과 같은 고급 용어를 사용하지 마십시오. 그 의미를 모릅니다.

답변:


559

System.arraycopy ()를 사용해 볼 수 있습니다

int[] src  = new int[]{1,2,3,4,5};
int[] dest = new int[5];

System.arraycopy( src, 0, dest, 0, src.length );

그러나 대부분의 경우 clone ()을 사용하는 것이 좋습니다.

int[] src = ...
int[] dest = src.clone();

9
바퀴를 깎지 않은 경우 +1 그리고 내가 아는 한,이 솔루션은 어레이 복사에서 더 빨리 얻을 수 있습니다.
Felipe Hummel

6
복제 및 배열 복사는 모두 기본입니다. 클론이 조금 더 빠를 것으로 기대합니다. 차이가 중요하지 않습니다.
MeBigFatGuy

5
@Felipe, @MeBigFatGuy-큰 배열에만 해당됩니다. 작은 어레이의 경우 설정 오버 헤드로 인해 복사 루프가 더 빠를 수 있습니다. 에 대한 javadoc을 보면 System.arraycopy메소드가 시작하기 전에 다양한 사항을 확인해야 함을 알 수 있습니다. 정적 배열 유형에 따라 복사 루프에서는 이러한 검사 중 일부가 필요하지 않습니다.
Stephen C

7
@FelipeHummel, @MeBigFatGuy, @StephenC-여기 답변에 언급 된 배열 복사 방법성능 테스트가 있습니다. 이 설정 clone()에서 250,000 요소에 가장 빠른 것으로 판명되었습니다.
Adam

6
여기서 논의되는 모든 논의는 시간의 99.999 %가 중요하지 않은 마이크로 성능 문제에 관한 것입니다. 더 중요한 점은 src.clone()새로운 배열을 할당하고 수행하는 것보다 읽기 쉽고 오류 가능성이 훨씬 적다는 것 arraycopy입니다. (또한 빠릅니다.)
Brian Goetz

231

당신이 사용할 수있는

int[] a = new int[]{1,2,3,4,5};
int[] b = a.clone();

게다가.


6
" A가 [6,7,8,9,10]으로 변경 되더라도 B는 여전히 [1,2,3,4,5] 여야합니다 ." OP는 루프를 사용해 보았지만 효과가 없었습니다.
Harry Joy

15
캐스트는 불필요합니다. 좋은 정적 분석기가 경고합니다. 그러나 복제는 어레이의 새로운 사본을 만드는 가장 좋은 방법입니다.
erickson

5
@MeBigFatGuy-OP의 사용 사례는 동일한 어레이에 반복 복사를 수반하므로 복제가 작동하지 않습니다.
Stephen C

4
@ Stephen C, 나는 그것을 읽지 못했습니다. 나는 단지 그가 사본을 원한다는 것을 읽었고 그 후에 비정품 버전을 반복적으로 업데이트 할 것입니다.
MeBigFatGuy

4
@MeBigFatGuy-그는 "나는 지속적으로 업데이트되는 어레이 A를 가지고있다" 고 말했다 . . 어쩌면 나는 그것에 대해 너무 많이 읽고 있지만, 그가 A를 B에 반복적으로 복사하고 있음을 암시합니다.
Stephen C

184

사본을 만들려면 다음을 수행하십시오.

int[] a = {1,2,3,4,5};

이것은 갈 길입니다.

int[] b = Arrays.copyOf(a, a.length);

Arrays.copyOfa.clone()작은 배열 보다 빠를 수 있습니다 . 두 copy 요소가 똑같이 빠르지 만 clone ()이 반환 Object되므로 컴파일러는에 암시 적 캐스트를 삽입해야 int[]합니다. 바이트 코드에서 다음과 같이 볼 수 있습니다.

ALOAD 1
INVOKEVIRTUAL [I.clone ()Ljava/lang/Object;
CHECKCAST [I
ASTORE 2

63

http://www.journaldev.com/753/how-to-copy-arrays-in-java의 멋진 설명

자바 배열 복사 방법

Object.clone () : Object 클래스는 clone () 메소드를 제공하며 java의 array도 Object이므로이 메소드를 사용하여 전체 배열 복사를 수행 할 수 있습니다. 배열의 부분 복사본을 원할 경우이 방법이 적합하지 않습니다.

System.arraycopy () : 시스템 클래스 arraycopy ()는 배열의 부분 복사를 수행하는 가장 좋은 방법입니다. 복사 할 총 요소 수와 소스 및 대상 배열 인덱스 위치를 쉽게 지정할 수 있습니다. 예를 들어 System.arraycopy (source, 3, destination, 2, 5) 는 소스의 3 번째 인덱스부터 목적지의 2 번째 인덱스까지 5 개의 요소를 소스에서 대상으로 복사합니다.

Arrays.copyOf () : 배열의 처음 몇 요소 또는 배열의 전체 사본을 복사하려는 경우이 메소드를 사용할 수 있습니다. 분명히 System.arraycopy ()와 같이 다재다능하지는 않지만 혼란스럽고 사용하기 쉽지 않습니다.

Arrays.copyOfRange () : 시작 인덱스가 0이 아닌 배열의 요소를 거의 복사하지 않으려면이 메서드를 사용하여 부분 배열을 복사 할 수 있습니다.


35

이 "배열을 복사하는 더 좋은 방법"이 실제로 문제를 해결하지 못할 것이라고 생각합니다.

당신은 말한다

[...]와 같은 for 루프를 시도했지만 제대로 작동하지 않는 것 같습니다.

해당 루프를 살펴보면 작동하지 않는 확실한 이유 는 없습니다.

  • 당신은 어떻게 든이 ab배열이 엉망 (예 ab같은 배열을 참조), 또는
  • 응용 프로그램이 다중 스레드이며 다른 스레드가 a동시에 배열을 읽고 업데이트합니다 .

두 경우 모두 복사를 수행하는 대체 방법으로는 근본적인 문제가 해결되지 않습니다.

첫 번째 시나리오에 대한 해결책은 분명합니다. 두 번째 시나리오에서는 스레드를 동기화하는 방법을 찾아야합니다. 원자 배열 클래스는 원자 복사 생성자 또는 복제 메소드가 없기 때문에 도움이되지 않지만 원시 뮤텍스를 사용하여 동기화하면 트릭이 수행됩니다.

(귀하의 질문에는 이것이 실제로 스레드와 관련이 있다고 생각하게하는 힌트가 있습니다 (예 : a끊임없이 변화하는 진술 ).)


2
.. 아마 사실이다.
MeBigFatGuy


9

배열에서 길이를 호출하는 모든 솔루션은 코드 중복 null 체커를 추가하십시오.

int[] a = {1,2,3,4,5};
int[] b = Arrays.copyOf(a, a.length);
int[] c = a.clone();

//What if array a comes as local parameter? You need to use null check:

public void someMethod(int[] a) {
    if (a!=null) {
        int[] b = Arrays.copyOf(a, a.length);
        int[] c = a.clone();
    }
}

휠을 발명하지 말고 필요한 모든 점검이 이미 수행 된 유틸리티 클래스를 사용하는 것이 좋습니다. 아파치 커먼즈의 ArrayUtils를 고려하십시오. 코드가 짧아집니다.

public void someMethod(int[] a) {
    int[] b = ArrayUtils.clone(a);
}

여기에서 찾을 수있는 Apache Commons


8

당신은 또한 사용할 수 있습니다 Arrays.copyOfRange .

:

public static void main(String[] args) {
    int[] a = {1,2,3};
    int[] b = Arrays.copyOfRange(a, 0, a.length);
    a[0] = 5;
    System.out.println(Arrays.toString(a)); // [5,2,3]
    System.out.println(Arrays.toString(b)); // [1,2,3]
}

이 방법은와 비슷 Arrays.copyOf하지만 더 유연합니다. 둘 다 사용합니다System.arraycopy 후드 아래에서 합니다.

참조 :


3

null 안전 배열의 복사본의 Object.clone()경우이 답변에 제공된 방법 과 함께 선택 사항을 사용할 수도 있습니다 .

int[] arrayToCopy = {1, 2, 3};
int[] copiedArray = Optional.ofNullable(arrayToCopy).map(int[]::clone).orElse(null);

이 솔루션이 지나치게 복잡하다는 사실에도 불구하고 메모리 낭비가 발생하고 어레이에 비밀 (예 : 비밀번호가있는 바이트 배열)이 포함 된 경우 가비지 콜렉션이 노출 될 때까지 중간 오브젝트가 힙에 상주하므로 보안 결함이 발생합니다. 공격자에게.
Weltraumschaf

1
배열 이이 구성을 위해 특별히 힙에 있다는 데 동의하지 않습니다. 실제로 필요할 때만 clone을 호출하고 Optional객체는 기존 배열에 대한 참조가있는 빈 객체 일뿐입니다. 성능에 미치는 영향에 대해, 실제로 이러한 유형의 구성은 JVM 내부에 인라인하기에 적합한 후보이며 다른 방법보다 더 많은 영향을 미치지 않기 때문에 실제로 영향을 미치는 것으로 판단하기에는 너무 이르다. 더 복잡하거나 그렇지 않은 것으로 간주하는 것은 스타일 문제 (기능적 프로그래밍 대 절차 적 프로그래밍뿐 아니라 절차 적 프로그래밍)입니다.
Nicolas Henneaux

3

당신이없는 원시 배열 함께 작업해야하는 경우 ArrayList다음 Arrays당신이 필요가 있습니다. 소스 코드를 보면 배열 사본을 얻는 가장 좋은 방법입니다. 그들은 방어적인 프로그래밍을 잘하고 있기 때문에System.arraycopy()비논리적 매개 변수를 제공하는 경우이 메소드는 검사되지 않은 예외를 많이 발생 있습니다.

Arrays.copyOf()첫 번째 Nth요소에서 새로운 짧은 배열로 복사하는 것을 사용할 수 있습니다 .

public static <T> T[] copyOf(T[] original, int newLength)

사본이 지정된 길이를 갖도록 지정된 배열을 복사하거나 널 (필요한 경우)로 채 웁니다. 원본 배열과 복사본 모두에 유효한 모든 인덱스의 경우 두 배열에 동일한 값이 포함됩니다. 사본에서 유효하지만 원본에서는 유효하지 않은 색인의 경우 사본에는 널이 포함됩니다. 이러한 길이는 지정된 길이가 원래 배열의 길이보다 큰 경우에만 존재합니다. 결과 배열은 원래 배열과 정확히 동일한 클래스입니다.

2770
2771    public static <T,U> T[] More ...copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
2772        T[] copy = ((Object)newType == (Object)Object[].class)
2773            ? (T[]) new Object[newLength]
2774            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
2775        System.arraycopy(original, 0, copy, 0,
2776                         Math.min(original.length, newLength));
2777        return copy;
2778    }

또는 Arrays.copyOfRange() 트릭을 수행합니다.

public static <T> T[] copyOfRange(T[] original, int from, int to)

지정된 배열의 지정된 범위를 새 배열로 복사합니다. 범위 (시작)의 초기 색인은 0에서 original.length (포함) 사이에 있어야합니다. original [from]의 값은 (== original.length 또는 == to가 아닌 한) 복사본의 초기 요소에 배치됩니다. 원래 배열의 후속 요소 값은 사본의 후속 요소에 배치됩니다. from (보다 크거나 같아야하는) 범위 (to)의 최종 인덱스는 original.length보다 클 수 있습니다.이 경우 인덱스가 original보다 크거나 같은 사본의 모든 요소에 null이 배치됩니다. 길이-부터. 반환 된 배열의 길이는 ~입니다. 결과 배열은 원래 배열과 정확히 동일한 클래스입니다.

3035    public static <T,U> T[] More ...copyOfRange(U[] original, int from, int to, Class<? extends T[]> newType) {
3036        int newLength = to - from;
3037        if (newLength < 0)
3038            throw new IllegalArgumentException(from + " > " + to);
3039        T[] copy = ((Object)newType == (Object)Object[].class)
3040            ? (T[]) new Object[newLength]
3041            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
3042        System.arraycopy(original, from, copy, 0,
3043                         Math.min(original.length - from, newLength));
3044        return copy;
3045    }

보시다시피, 둘 다 래퍼 함수입니다. System.arraycopy 당신이하려고하는 것이 유효하다는 방어 논리를 가진 입니다.

System.arraycopy 배열을 복사하는 가장 빠른 방법입니다.


0

나는 2D 배열과 비슷한 문제가 있었고 여기서 끝났습니다. 주 배열을 복사하고 내부 배열의 값을 변경하고 있었고 두 사본에서 값이 변경되면 놀랐습니다. 기본적으로 두 사본 모두 독립적이지만 동일한 내부 배열에 대한 참조가 포함되어 있었고 원하는 것을 얻기 위해 내부 배열의 사본 배열을 만들어야했습니다.

아마도 OP의 문제는 아니지만 여전히 도움이되기를 바랍니다.

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