μ΄μ κΈμ ν΅ν΄ λΆλ³ κ°μ²΄μμ cloneμ μ¬μ©νλ λ°©λ²μ μμ보μλ€.
μ΄λ²μλ κ°λ³ κ°μ²΄μμ cloneμ μ¬μ©νλ λ°©λ²μ μμ보μ. κ°λ³ κ°μ²΄λ₯Ό 볡μ νλ 3κ°μ§ λ°©λ²μ μ΄ν΄λ³΄μ.
1. λ°°μ΄ λ³΅μ¬λ₯Ό μ΄μ©νλ κ²½μ° π¨π
public class Stack implements Cloneable{
private Object[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
public Stack() {
this.elements = new Object[DEFAULT_INITIAL_CAPACITY];
}
public void push(Object e) {
ensureCapacity();
elements[size++] = e;
}
public Object pop() {
if(size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null; // λ€ μ΄ μ°Έμ‘° ν΄μ
return result;
}
// μμλ₯Ό μν 곡κ°μ μ μ΄λ νλ μ΄μ ν보νλ€.
private void ensureCapacity() {
if(elements.length == size)
elements = Arrays.copyOf(elements, 2 * size + 1);
}
public boolean isEmpty() {
return size == 0;
}
@Override
public Stack clone() {
try {
Stack result = (Stack) super.clone();
return result;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}
----------
public static void main(String[] args) {
Object[] values = new Object[2];
values[0] = new User("amenable", 24);
values[1] = new User("sunny", 21);
Stack stackOrigin = new Stack();
for(Object arg : values)
stackOrigin.push(arg);
Stack stackCopy = stackOrigin.clone();
while(!stackOrigin.isEmpty())
System.out.println(stackOrigin.pop() + " ");
// clone.User@15db9742
// clone.User@6d06d69c
while(!stackCopy.isEmpty())
System.out.println(stackCopy.pop() + " ");
// null
// null
}
λΆλ³ κ°μ²΄μμ cloneμ μ μν λ°©μμΌλ‘ κ°λ³ κ°μ²΄μ μ μ©νκ² λλ€λ©΄ μμ κ°μ΄ copy λ μΈμ€ν΄μ€κ° κΈ°μ‘΄ κ°μ μν₯μ λ°κ² λλ€. κ·Έλ¬λ―λ‘ μμ κ°μ΄ λ°°μ΄μ 볡μ ν λλ λ°°μ΄μ clone λ©μλλ₯Ό μ¬μ©ν΄μΌ νλ€.
@Override
public Stack clone() {
try {
Stack result = (Stack) super.clone();
result.elements = elements.clone(); // finalμ΄λ©΄ μ¬μ© λΆκ°
return result;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
----------
public static void main(String[] args) {
Object[] values = new Object[2];
values[0] = new User("amenable", 24);
values[1] = new User("sunny", 21);
Stack stackOrigin = new Stack();
for(Object arg : values)
stackOrigin.push(arg);
Stack stackCopy = stackOrigin.clone();
while(!stackOrigin.isEmpty())
System.out.println(stackOrigin.pop() + " ");
// clone.User@15db9742
// clone.User@6d06d69c
while(!stackCopy.isEmpty())
System.out.println(stackCopy.pop() + " ");
// clone.User@15db9742
// clone.User@6d06d69c
}
μ΄λ κ² νλ©΄ λ°°μ΄μ΄ 2κ° μκΈ°κΈ΄ νκ² μ§λ§, κ²°κ΅μ μμ 볡μ¬(Shallow Copy)λ€. κ·Έλμ μ¬μ€μ λ°°μ΄μμ λμΌν μΈμ€ν΄μ€λ₯Ό λ³΄κ³ μκΈ΄ νλ€.
κ·Έλ¦¬κ³ elementsκ° finalμ΄λΌλ©΄ μλ‘ ν λΉν μ μμΌλ―λ‘ finalμ μΈ μ μλ€λ μ μ½λ μ‘΄μ¬νλ€.
2. Deep Copyλ₯Ό μ΄μ©νλ κ²½μ° π©π¬
public class HashTable implements Cloneable {
private Entry[] buckets = new Entry[10];
private static class Entry {
final Object key;
Object value;
Entry next;
Entry(Object key, Object value, Entry next) {
this.key = key;
this.value = value;
this.next = next;
}
public void add(Object key, Object value) {
this.next = new Entry(key, value, null);
}
public Entry deepCopy() {
return new Entry(key, value, next == null ? null : next.deepCopy());
}
}
@Override
public HashTable clone() {
HashTable result = null;
try {
result = (HashTable)super.clone();
result.buckets = this.buckets.clone(); // shallow copy λΌμ μννλ€
return result;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
public static void main(String[] args) {
HashTable hashTable = new HashTable();
Entry entry = new Entry(new Object(), new Object(), null);
hashTable.buckets[0] = entry;
HashTable clone = hashTable.clone();
System.out.println(hashTable.buckets[0] == entry); // true
System.out.println(hashTable.buckets[0] == clone.buckets[0]); // true
}
}
볡μ λ³Έμ μμ λ§μ λ²ν· λ°°μ΄μ κ°μ§λ§, μ΄ λ°°μ΄μ μλ³Έκ³Ό κ°μ μ°κ²° 리μ€νΈλ₯Ό μ°Έμ‘°νκ³ μλ€. κ·Έλ¬λ―λ‘ μλμ κ°μ΄ Deep Copyλ₯Ό ν΄μ€μΌ νλ€.
public Entry deepCopy() {
return new Entry(key, value, next == null ? null : next.deepCopy());
}
@Override
public HashTable clone() {
HashTable result = null;
try {
result = (HashTable)super.clone();
result.buckets = new Entry[this.buckets.length];
for(int i = 0; i < this.buckets.length; i++) {
if(buckets[i] != null) {
result.buckets[i] = this.buckets[i].deepCopy(); // deep copy
}
}
return result;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
----------
public static void main(String[] args) {
HashTable hashTable = new HashTable();
Entry entry = new Entry(new Object(), new Object(), null);
hashTable.buckets[0] = entry;
HashTable clone = hashTable.clone();
System.out.println(hashTable.buckets[0] == entry); // true
System.out.println(hashTable.buckets[0] == clone.buckets[0]); // false
}
μμμ λμ¨ DeepCopy λ°©λ²μ μ¬κ·μ μΌλ‘ νΈμΆνλ λ°©μμ΄λ€. μ€ν μ€λ²νλ‘μ° λλ¬Έμ μ°κ²° 리μ€νΈλ₯Ό 볡μ νλ λ°©λ²μΌλ‘λ κ·Έλ€μ§ μ’μ§ μλ€. κ·Έλ κΈ° λλ¬Έμ μλμ κ°μ΄ deepCopy λ©μλλ₯Ό μ¬κ· νΈμΆ λμ λ°λ³΅μλ₯Ό μ¨μ μννλ λ°©λ²μΌλ‘ μμ ν μ μλ€.
public Entry deepCopy() {
Entry result = new Entry(key, value, next);
for(Entry p = result; p.next != null; p = p.next) {
p.next = new Entry(p.next.key, p.next.value, p.next.next);
}
return result;
}
κ·Έλ¦¬κ³ cloneλ©μλ μμμλ μλμ κ°μ΄ μ¬μ μ ν μ μλ λ©μλλ₯Ό λ§λ€μ§ μμμΌ νλ€. κ·Έ μ΄μ λ νμ ν΄λμ€μμ ν΄λΉ λ©μλλ₯Ό μ¬μ μνλ©΄ λμμ΄ λ°λ μ μκΈ° λλ¬Έμ΄λ€.
@Override
public HashTable clone() {
HashTable result = null;
try {
result = (HashTable)super.clone();
// result.buckets = new Entry[this.buckets.length];
result.buckets = createNewBuckets();
for(int i = 0; i < this.buckets.length; i++) {
if(buckets[i] != null) {
result.buckets[i] = this.buckets[i].deepCopy(); // deep copy
}
}
return result;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
protected Entry[] createNewBuckets() {
...
}
3. κ³ μμ€ λ©μλλ₯Ό μ΄μ©νλ κ²½μ° π¦ΈβοΈ
// κ³ μμ€ λ©μλ μμ
result.get(key);
result.put(key, value);
super.cloneμ νΈμΆνμ¬ μ»μ κ°μ²΄μ λͺ¨λ νλλ₯Ό μ΄κΈ° μνλ‘ μ€μ νκ³ μλ³Έ κ°μ²΄μ μνλ₯Ό λ€μ μμ±νλ κ³ μμ€ λ©μλλ₯Ό μ¬μ©ν μλ μλ€. κ·Έλ¬λ©΄ μλ³Έκ³Ό 볡μ¬λ³Έμ λ΄μ©μ΄ κ°κ² λλ€.
νμ§λ§ μ΄ λ°©λ²μ μ μμ€μμ λ°λ‘ μ²λ¦¬ν λλ³΄λ€ λ리λ€. κ·Έλ¦¬κ³ Cloneable μν€ν μ²μ κΈ°μ΄κ° λλ νλ λ¨μ κ°μ²΄ 볡μ¬λ₯Ό μ°ννκΈ° λλ¬Έμ μ 체 Cloneable μν€ν μ²μλ μ΄μΈλ¦¬μ§ μλ λ°©μμ΄κΈ°λ νλ€.
4. μΆκ° μ£Όμ μ¬ν π¨π»
1. μμμ© ν΄λμ€λ Cloneableμ ꡬνν΄μλ μ λλ€.
κ·Έ μ΄μ λ ν΄λΉ ν΄λμ€λ₯Ό νμ₯νλ €λ νλ‘κ·Έλλ¨Έμκ² cloneμ μ¬λ°λ₯΄κ² ꡬννλλ‘ λ§μ λΆλ΄μ μ£ΌκΈ° λλ¬Έμ΄λ€. κ·Έλμ μλμ κ°μ 2κ°μ§ λ°©λ²μΌλ‘ κ·Έ λΆλ΄μ λμ΄μ€ μ μλ€.
- μΆμν΄λμ€μμ cloneμ ꡬνν΄ μ£Όκ³ νμ ν΄λμ€μμ ꡬνμ νμ§ μλλ‘ νκΈ°.
- νμν΄λμ€μμ μμ cloneμ ꡬννμ§ λͺ»νλλ‘ λ§κΈ°
μ΄ λ°©μμ λ§μΉ Objectλ₯Ό λ°λ‘ μμν λμ²λΌ Cloneable ꡬν μ¬λΆλ₯Ό νμ ν΄λμ€μμ μ ννλλ‘ ν΄μ€λ€.
public abstract class Shape implements Cloneable {
private int area;
public abstract int getArea();
// λ°©λ² 1
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
// λ°©λ² 2
@Override
protected final Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
}
2. μ€λ λ μμ ν΄λμ€λ‘ λ§λ€μ΄ μ€μΌ νλ€λ©΄ λκΈ°νλ μ²λ¦¬ν΄μ€μΌ νλ€.
@Override
public synchronized HashTable clone() {
...
}
3. Deep Copy λ°©λ²μμ μ΄ν΄λ³Έ κ²μ²λΌ cloneλ©μλ μμμλ μ¬μ μ ν μ μλ λ©μλλ₯Ό λ§λ€μ§ μμμΌ νλ€.
5. κ²°λ‘ π¨π
μ§κΈκΉμ§ μ μ½μ΄μλ€λ©΄ λκΌκ² μ§λ§, κ΅³μ΄ μ΄λ κ² ν΄μΌ ν κΉ? λ§€μ° λ³΅μ‘ν κ²½μ°κ° λ§λ€...
Cloneableμ μ΄λ―Έ ꡬνν ν΄λμ€λ₯Ό νμ₯νλ€λ©΄ μ΄μ© μ μμ΄ cloneμ μ μλνλλ‘ κ΅¬νν΄μΌ νλ€. κ·Έλ μ§ μμ μν©μμλ μλμ κ°μ΄ λ³΅μ¬ μμ±μ(= λ³ν μμ±μ)μ λ³΅μ¬ ν©ν°λ¦¬(= λ³ν ν©ν°λ¦¬)λΌλ λ λμ κ°μ²΄ λ³΅μ¬ λ°©μμ μ¬μ©νλλ‘ νμ.
public class User {
private final String name;
private final int age;
public User(String name, int age) {
super();
this.name = name;
this.age = age;
}
// λ³΅μ¬ μμ±μ
public User(User user) {
this.name = user.name;
this.age = user.age;
}
// λ³΅μ¬ ν©ν°λ¦¬
public static User copyFactory(User user) {
User copyUser = new User(user.name, user.age);
return copyUser;
}
}
λ³΅μ¬ μμ±μμ λ³΅μ¬ ν©ν°λ¦¬λ₯Ό μ΄μ©νλ€λ©΄ λ€μκ³Ό κ°μ μ₯μ μ΄ μλ€. (μ§κΈκΉμ§ κ³ λ €ν΄ μ€¬λ κ²λ€μ λ°λλΌκ³ μκ°νλ©΄ μ΄ν΄νκΈ° μ¬μΈ κ²μ΄λ€.)
- μΈμ΄ λͺ¨μμ μ΄κ³ μνμ²λ§ν κ°μ²΄ μμ± λ©μ»€λμ¦(μμ±μλ₯Ό μ°μ§ μλ λ°©μ)μ μ¬μ©νμ§ μμλ λλ€.
- μμ±νκ² λ¬Έμνλ κ·μ½μ κΈ°λμ§ μμλ λλ€. (clone κ·μ½)
- μ μμ μΈ final νλ μ©λ²κ³Ό μΆ©λνμ§ μλλ€.
- λΆνμν κ²μ¬ μμΈλ₯Ό λμ§μ§ μλλ€.
- νλ³νλ νμνμ§ μλλ€.
- 'μΈν°νμ΄μ€' νμ
μ μΈμ€ν΄μ€λ₯Ό μΈμλ‘ λ°μ μ μλ€.
μλ³Έμ ꡬν νμ μ μ½λ§€μ΄μ§ μκ³ λ³΅μ λ³Έμ νμ μ μ§μ μ νν μ μλ€.
public static void main(String[] args) {
Set<String> hashSet = new HashSet<>();
Set<String> treeSet = new TreeSet<>(hashSet);
}
----------
// μΈμλ₯Ό νμΈνμ.
public TreeSet(Collection<? extends E> c) {
this();
addAll(c);
}
ν΄λΉ κΈμ λ°±κΈ°μ λμ 'μ΄νν°λΈ μλ° μλ²½ 곡λ΅'μ μκ°νκ³ μμ±ν κ²μ λλ€.