어제 오늘 내일

[Java] ArrayList 복사하기 (Shallow Copy, Deep Copy) 본문

IT/Java

[Java] ArrayList 복사하기 (Shallow Copy, Deep Copy)

hi.anna 2021. 5. 15. 23:29

 

 

ArrayList 객체를 복사하는 방법 2가지를 소개합니다.

  1. ArrayList 복사하기 - clone() - Shallow Copy
  2. ArrayList 복사하기 - Deep Copy

 

 

1. ArrayList 복사하기 - clone() - Shallow Copy

public Object clone()

보통 ArrayList를 복사할 때,

ArrayList의 clone() 메소드를 사용합니다.

이 메소드는 ArrayList 객체를 shallow copy한 복사본을 리턴합니다.

 

  예제 1 - clone() 후, ArrayList의 데이터 변경  

import java.util.ArrayList;

public class CloneArrayList {
    public static void main(String[] args) {

        // 1. ArrayList 준비
        ArrayList<Fruit> fruitList = new ArrayList<Fruit>();
        fruitList.add(new Fruit("Apple", 1000));
        fruitList.add(new Fruit("Banana", 2000));

        // 2. ArrayList 복사 - clone()
        ArrayList<Fruit> copyOfFruitList = (ArrayList<Fruit>) fruitList.clone();
        
        // 3. 복사된 결과 출력
        System.out.println("=== ArrayList 복사(clone()) ===");
        System.out.println("fruitList : " + fruitList);  // [[ Apple: 1000 ], [ Banana: 2000 ]]
        System.out.println("copyOfFruitList : " + copyOfFruitList); // [[ Apple: 1000 ], [ Banana: 2000 ]]

        // 4. 원본 ArrayList 변경
        fruitList.add(new Fruits("Orange", 500));

        // 5. 결과 출력
        System.out.println("=== 원본 ArrayList 변경 ===");
        System.out.println("fruitList : " + fruitList);  // [[ Apple: 1000 ], [ Banana: 2000 ], [ Orange: 500 ]]
        System.out.println("copyOfFruitList : " + copyOfFruitList); // [[ Apple: 1000 ], [ Banana: 2000 ]]
    }
}

class Fruit {

    private String name;
    private int price;

    public Fruit(String name, int price) {
        this.name = name;
        this.price = price;
    }

    @Override
    public String toString() {
        return "[ " + this.name + ": " + this.price + " ]";
    }
}

  예제 1 - 결과  

=== ArrayList 복사(clone()) ===
fruitList : [[ Apple: 1000 ], [ Banana: 2000 ]]
copyOfFruitList : [[ Apple: 1000 ], [ Banana: 2000 ]]

=== 원본 ArrayList 변경 ===
fruitList : [[ Apple: 1000 ], [ Banana: 2000 ], [ Orange: 500 ]]
copyOfFruitList : [[ Apple: 1000 ], [ Banana: 2000 ]]

clone() 메소드를 사용하여 ArrayList를 복사하는 예제입니다.

0. Fruit Class 생성

먼저 Fruit 클래스를 작성하였습니다.

이 클래스는 name, price 속성을 가지고 있습니다.

 

1. 원본 ArrayList 생성

ArrayList<Fruit> fruitList = new ArrayList<Fruit>();

fruitList.add(new Fruit("Apple", 1000);

fruitList.add(new Fruit("Banana", 2000);

Fruit 객체를 element로 갖는 원본 ArrayList를 생성하고,

데이터를 추가하였습니다.

 

2. ArrayList 복사 - clone()

ArrayList<Fruit> copyOfFruitList = (ArrayList<Fruit>)fruitList.clone();

clone() 메소드를 사용하여, ArrayList 객체를 복사하였습니다.

 

3. 복사된 결과 출력

원본 ArrayList인 fruitList와

복사된 ArrayList인 copyOfFruitList를 출력하였습니다.

두, 객체의 값이 동일한 것을 확인 할 수 있습니다.

 

4. 원본 ArrayList 변경

fruitList.add(new Fruit("Orange", 500));

원본 ArrayList에 orange라는 Fruit 객체를 하나도 추가하였습니다.

 

5. 결과 출력

원본 ArrayList에만 새로운 Fruit 객체인 "orange"가 추가되고,

복사본에는 추가되지 않은 것을 확인 할 수 있습니다.

 

 

  예제 2 - clone() 후, fruit 객체의 데이터 변경  

그런데, ArrayList 원소의 추가, 삭제, 변경이 아닌,

ArrayList의 원소인 apple, banana 객체의 price값이 변경된다면 어떻게 될까요?

import java.util.ArrayList;

public class CloneArrayList {
    public static void main(String[] args) {

        // 1. ArrayList 준비
        ArrayList<Fruit> fruitList = new ArrayList<Fruit>();
        fruitList.add(new Fruit("Apple", 1000));
        fruitList.add(new Fruit("Banana", 2000));

        // 2. ArrayList 복사 - clone()
        ArrayList<Fruit> copyOfFruitList = (ArrayList<Fruit>) fruitList.clone();
        
        // 3. 복사된 결과 출력
        System.out.println("=== ArrayList 복사(clone()) ===");
        System.out.println("fruitList : " + fruitList);  // [[ Apple: 1000 ], [ Banana: 2000 ]]
        System.out.println("copyOfFruitList : " + copyOfFruitList); // [[ Apple: 1000 ], [ Banana: 2000 ]]

        // 4. 원본 ArrayList의 apple의 price 변경
        Fruit apple = fruitList.get(0);
        apple.setPrice(100);

        // 5. 결과 출력
        System.out.println("=== 원본 ArrayList의 apple의 price 변경 ===");
        System.out.println("fruitList : " + fruitList);  // [[ Apple: 100 ], [ Banana: 2000 ]]
        System.out.println("copyOfFruitList : " + copyOfFruitList); // [[ Apple: 100 ], [ Banana: 2000 ]]
    }
}

class Fruit {

    private String name;
    private int price;

    public Fruit(String name, int price) {
        this.name = name;
        this.price = price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "[ " + this.name + ": " + this.price + " ]";
    }
}

  예제 2 - 결과  

=== ArrayList 복사(clone()) ===
fruitList : [[ Apple: 1000 ], [ Banana: 2000 ]]
copyOfFruitList : [[ Apple: 1000 ], [ Banana: 2000 ]]

=== 원본 ArrayList의 apple의 price 변경 ===
fruitList : [[ Apple: 100 ], [ Banana: 2000 ]]
copyOfFruitList : [[ Apple: 100 ], [ Banana: 2000 ]]

위 예제에서는 원본 ArrayList에 포함된 apple 객체의 가격을

아래와 같이 변경하였습니다.

Fruit apple = fruitList.get(0);

apple.setPrice(100);

fruitList(원본 ArrayList)의 0번째 원소인 fruit 객체의 가격을

1000원에서 100원으로 수정하였습니다.

 

변경후 결과를 보면,

원본 ArrayList(fruitList) 와 복사본 ArrayList(copyOfFruitList)의

apple 객체의 가격이 모두 변경된 것을 확인 할 수 있습니다.

 

 

ArrayList에 객체를 넣으면,

ArrayList는 해당 객체를 가리키는 주소를 ArrayList에 저장합니다.

따라서, primitive 타입이 아닌, 객체를 clone()하면

ArrayList가 가진 객체의 주소를 복사하여,

복사된 ArrayList도 같은 객체를 가리키게 되는 것이죠.

따라서, 원본 ArrayList의 apple이라는 이름을 가지는 fruit 객체의 값을 수정하면,

복사본이 가리키는 객체는 동일하므로, 

마치 복사본 ArrayList의 객체도 같이 변경된 것처럼 보이는 것입니다.

 

 

 

2. ArrayList 복사하기 - Deep Copy

ArrayList 안에 들어있는 element 객체까지 모두 복사해 주고 싶으면,

대상 객체에  cloneable 을 implement 하고,

clone() 메소드를 override하여,

ArrayList 안의 각각의 객체들을 clone() 해 주어야 합니다.

 

  코드  

import java.util.ArrayList;

public class CloneArrayList {
    public static void main(String[] args) throws CloneNotSupportedException {

        // 1. ArrayList 준비
        ArrayList<Fruit> fruitList = new ArrayList<Fruit>();
        fruitList.add(new Fruit("Apple", 1000));
        fruitList.add(new Fruit("Banana", 2000));

        // 2. ArrayList 복사 - 반복문과 객체의 clone() 이용
        ArrayList<Fruit> copyOfFruitList = new ArrayList<Fruit>();
        for (Fruit f : fruitList) {
            copyOfFruitList.add(f.clone());
        }

        // 3. 복사된 결과 출력
        System.out.println("=== ArrayList 복사(반복문과 객체의 clone() 이용) ===");
        System.out.println("fruitList : " + fruitList); // [[ Apple: 1000 ], [ Banana: 2000 ]]
        System.out.println("copyOfFruitList : " + copyOfFruitList); // [[ Apple: 1000 ], [ Banana: 2000 ]]

        // 4. 원본 ArrayList의 apple의 price 변경
        Fruit apple = fruitList.get(0);
        apple.setPrice(100);

        // 5. 결과 출력
        System.out.println("=== 원본 ArrayList의 apple의 price 변경 ===");
        System.out.println("fruitList : " + fruitList); // [[ Apple: 100 ], [ Banana: 2000 ]]
        System.out.println("copyOfFruitList : " + copyOfFruitList); // [[ Apple: 100 ], [ Banana: 2000 ]]
    }
}

class Fruit implements Cloneable {

    private String name;
    private int price;

    public Fruit(String name, int price) {
        this.name = name;
        this.price = price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "[ " + this.name + ": " + this.price + " ]";
    }

    @Override
    protected Fruit clone() throws CloneNotSupportedException {
        return (Fruit) super.clone();
    }
}

  결과  

=== ArrayList 복사(반복문과 객체의 clone() 이용) ===
fruitList : [[ Apple: 1000 ], [ Banana: 2000 ]]
copyOfFruitList : [[ Apple: 1000 ], [ Banana: 2000 ]]

=== 원본 ArrayList의 apple의 price 변경 ===
fruitList : [[ Apple: 100 ], [ Banana: 2000 ]]
copyOfFruitList : [[ Apple: 1000 ], [ Banana: 2000 ]]

 

implements Cloneable, Override clone()

Fruit 클래스는 Cloneable 인터페이스를 implements하였습니다.

그리고, clone() 메소드를 Override 하였습니다.

이렇게하면, 객체의 clone() 메소드를 사용하여, 객체를 복사할 수 있습니다.

 

for(Fruit f : fruitList) {

   copyOfFruitList.add(f.clone());

}

이렇게, ArrayList에 들어갈 클래스를 정의한 후,

반복문과 Fruit 클래스의 clone()를 이용하여,

element(Fruit객체)를 하나씩 새로운 배열에 복사해 줍니다.

 

이렇게 하면,

원본 배열 안의 fruit 객체의 price를 변경하더라도,

복사본의 fruit 객체는 변경되지 않습니다.

 


 

ArrayList의 원소를 복사하는 2가지 방법을 알아보았습니다.

 

 

반응형
Comments