Java ArrayList 사본


214

나는이 ArrayList l1크기 10. I 어사 l1새 목록 참조 유형을 l2. 윌 l1l2같은 가리킨 ArrayList객체? 또는 ArrayList객체 의 사본이에 할당되어 l2있습니까?

l2참조를 사용할 때 목록 객체를 업데이트하면 l1참조 유형 의 변경 사항도 반영 됩니다.

예를 들면 다음과 같습니다.

List<Integer> l1 = new ArrayList<Integer>();
for (int i = 1; i <= 10; i++) {
    l1.add(i);
}

List l2 = l1;
l2.clear();

2 개의 목록 객체를 생성하고 컬렉션에서 오래된 것에서 새로운 것으로 복사하는 것을 제외하고 새로운 참조 변수에리스트 객체의 복사본을 할당하는 다른 방법이 있습니까?

답변:


460

예, 할당은 (참조 인) 의 을에 복사합니다 . 둘 다 동일한 객체를 참조합니다.l1l2

얕은 사본을 만드는 것은 매우 쉽습니다.

List<Integer> newList = new ArrayList<>(oldList);

(한 가지 예일뿐입니다.)


1
효율적으로 arraylist의 일부만 새 arraylist로 복사 할 수 있습니까? 예를 들어, 한 배열 목록에서 다른 새 배열 목록으로 위치 5와 10 사이의 요소를 복사하십시오. 내 응용 프로그램에서 범위가 훨씬 더 커질 것입니다.
Ashwin

1
@Ashwin : 음, 그것은 O (N) 연산이지만, 그렇습니다 ... 당신은 List.subList원래 목록의 한 부분에 대한 "보기"를 얻을 수 있습니다 .
Jon Skeet

배열 목록이 중첩되면 어떻게 (ArrayList<ArrayList<Object>>)됩니까? 이것은 모든 자식 ArrayList 객체의 사본을 재귀 적으로 생성합니까?
Cat

3
@ 고양이 : 아니요. 이것은 단지 얕은 사본입니다.
Jon Skeet

1
@ ShanikaEdiriweera : 스트림과 함께 유창하게 할 수 있습니다. 그러나 까다로운 부분은 대부분의 객체가 제공하지 않는 딥 카피를 만드는 것입니다. 특정 사례를 염두에두고 있으면 세부적인 내용으로 새로운 질문을하는 것이 좋습니다.
Jon Skeet

67

14
왜 이것이 더 바람직한 지 설명해 주 new ArrayList<>(source);시겠습니까?
Alex

6
@atc 새로운 ArrayList () 대신 얕은 복사를 수행하는 또 다른 방법입니다. 또 다른 알고리즘을 사용했으며 ArrayList뿐만 아니라 모든 List 구현에 사용할 수 있습니다. :
Sergii Zagriichuk

14
이 방법은 매우 오도됩니다! 실제로는 설명입니다. "하나의 소스 목록에서 대상으로 요소를 복사합니다"라고되어 있지만 복사되지는 않습니다! 그것들은 참조되므로 객체의 사본이 1 개만 있고 변경 가능하면 문제가 있습니다
ACV

5
Java-api 딥 클로닝의 어느 곳에서도 컬렉션 클래스에 의해 수행되지 않습니다
Vikash

이 답변은 의미가 없습니다. Collections.copy에 대한 대안은 아닙니다 new ArrayList<>(source). 무엇 Collections.copy실제로하는 일은 그 가정입니다 destination.size()같은 큰로서 적어도이며 source.size(), 다음 범위 지수별로 색인을 사용하여 복사 set(int,E)방법을. 이 메소드는 대상에 새 요소를 추가 하지 않습니다 . Javadoc에서 명확하지 않은 경우 소스 코드를 참조하십시오.
Radiodef

35

l1, l2동일한 참조, 동일한 객체를 가리 킵니다.

다른 ArrayList를 기반으로 새 ArrayList를 작성하려면 다음을 수행하십시오.

List<String> l1 = new ArrayList<String>();
l1.add("Hello");
l1.add("World");
List<String> l2 = new ArrayList<String>(l1); //A new arrayList.
l2.add("Everybody");

결과에는 l1여전히 2 개의 요소가 있고 l23 개의 요소가 있습니다.


당신은 차이를 설명시겠습니까 List<String> l2 = new ArrayList<String>(l1)List<String> l2 = l1?
MortalMan

@MortalMan의 차이점은 l2 = new ArrayList <String> (l1)은 완전히 새로운 객체이며 l2를 수정해도 l1에 영향을 미치지 않지만 List <String> l2 = l1은 새로운 객체를 만들지 않고 동일한 것을 참조한다는 것입니다 이 경우 l2.add ( "Everybody")와 같은 작업을 수행하면 l1.size () 및 l2.size ()는 모두 동일한 객체를 참조하므로 3을 반환합니다.
Alfredo Osorio

19

src ArrayList에서 dest Arraylist로 값을 복사하는 또 다른 편리한 방법은 다음과 같습니다.

ArrayList<String> src = new ArrayList<String>();
src.add("test string1");
src.add("test string2");
ArrayList<String> dest= new ArrayList<String>();
dest.addAll(src);

이것은 단지 참조의 복사가 아니라 실제 값의 복사입니다.


10
이것이 정확한지 확실하지 않습니다. 내 테스트는 반대를 보여줍니다 (여전히 동일한 객체를 참조 함)
invertigo

이 솔루션은 ArrayAdapter와 함께 ArrayList를 사용할 때 나를 위해 일했습니다.
albanx

1
이 답변은 잘못되었습니다. addAll ()은 invertigo가 말한 것처럼 참조를 복사합니다. 이것은 깊은 사본이 아닙니다.
jk7

ArrayList <String>의 경우 String은 변경할 수 없으므로이 대답은 허용되지만 OP의 예제 인 ArraList <Integer>로 시도하면 참조를 복사하는 것입니다.
jk7

내 하루가 아닌 것 같아 Integer 및 Long과 같은 클래스도 변경할 수 없으므로 Harshal의 답변은 ArrayList <Integer> 및 ArrayList <String>과 같은 간단한 경우에 작동합니다. 그것이 실패한 곳은 변경할 수없는 복잡한 객체입니다.
jk7

8

하나의 ArrayList를 다른 ArrayList로 복사하는 목적으로 사용되는 addAll () 메소드가 있습니다 .

예를 들어 sourceListtargetList의 두 가지 Array List가 있습니다. 아래 코드를 사용하십시오.

targetList.addAll (sourceList);


또한 참조를 복사합니다.
Vikash

4

Java는 객체를 전달하지 않고 참조 (포인터)를 객체에 전달합니다. 예, l2와 l1은 같은 객체에 대한 두 개의 포인터입니다.

동일한 내용을 가진 두 개의 다른 목록이 필요한 경우 명시적인 사본을 만들어야합니다.


3
"명시 적 사본"을 어떻게 만드나요? 당신이 깊은 사본에 대해 이야기하고 있다고 가정합니까?
Cin316 2016 년

1

List.copyOf ➙ 수정 불가능한 목록

당신은 물었다 :

목록의 사본을 할당 할 다른 방법이 없습니까?

Java 9는 List.of리터럴을 사용하여 수정 불가능한 List알 수없는 구체적인 클래스 를 만드는 메소드를 가져 왔습니다 .

LocalDate today = LocalDate.now( ZoneId.of( "Africa/Tunis" ) ) ;
List< LocalDate > dates = List.of( 
    today.minusDays( 1 ) ,  // Yesterday
    today ,                 // Today
    today.plusDays( 1 )     // Tomorrow
);

그것과 함께 우리는 또한 얻었다 List.copyOf. 이 메소드도 수정 불가능한 List콘크리트 클래스를 리턴합니다 .

List< String > colors = new ArrayList<>( 4 ) ;          // Creates a modifiable `List`. 
colors.add ( "AliceBlue" ) ;
colors.add ( "PapayaWhip" ) ;
colors.add ( "Chartreuse" ) ;
colors.add ( "DarkSlateGray" ) ;
List< String > masterColors = List.copyOf( colors ) ;   // Creates an unmodifiable `List`.

"수정 불가능"은 목록의 요소 수를 의미하며 각 슬롯에 요소로 보유 된 객체 참조는 고정되어 있습니다. 요소를 추가, 삭제 또는 교체 할 수 없습니다. 그러나 각 요소에 보유 된 객체 참조는 변경 가능 하거나 변경 불가능할 수 있습니다 .

colors.remove( 2 ) ;          // SUCCEEDS. 
masterColors.remove( 2 ) ;    // FAIL - ERROR.

코드는 IdeOne.com에서 실시간으로 실행 됩니다.

dates.toString () : [2020-02-02, 2020-02-03, 2020-02-04]

colors.toString () : [AliceBlue, 파파야 채찍, DarkSlateGray]

masterColors.toString () : [AliceBlue, PapayaWhip, Chartreuse, DarkSlateGray]

객체 참조에 대해 물었습니다. 다른 사람들이 말했듯이 하나의 목록을 만들어 두 개의 참조 변수 (포인터)에 할당하면 여전히 하나의 목록 만 있습니다. 둘 다 같은 목록을 가리 킵니다. 포인터를 사용하여 목록을 수정하면 메모리에 목록이 하나만 있기 때문에 나중에 두 포인터 모두 변경 사항을 볼 수 있습니다.

따라서 목록의 사본을 만들어야합니다. 해당 복사본을 수정할 수 없게하려면 List.copyOf이 답변에 설명 된 방법을 사용하십시오 . 이 접근 방식에서는 각각 동일한 내용 객체에 대한 참조를 보유하는 요소가있는 두 개의 개별 목록으로 끝납니다. 예를 들어, 위의 예제에서 String객체를 사용하여 색상을 나타내는 경우 색상 객체가 메모리에서 어딘가에 떠 있습니다. 두 목록에는 동일한 색상 객체에 대한 포인터가 있습니다. 다음은 다이어그램입니다.

여기에 이미지 설명을 입력하십시오

첫 번째 목록 colors은 수정 가능합니다. 이것은 위의 코드에서 볼 수 있듯이 원래의 세 번째 요소 Chartreuse(인덱스 2 = 서수 3)를 제거한 일부 요소가 제거 될 수 있음을 의미합니다 . 그리고 요소를 추가 할 수 있습니다. 그리고 요소는 몇 가지 다른 지점으로 변경 될 수 있습니다 String와 같은 OliveDrabCornflowerBlue.

대조적으로, 네 가지 요소 masterColors는 고정되어 있습니다. 다른 색상을 제거하거나 추가하거나 대체하지 않습니다. 그 List구현은 수정할 수 없습니다.

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