[Java] 数组的深拷贝
看到一个认为用clone方法对多维数组(数组的数组)进行深拷贝的代码,然后忘记了数组的clone方法并不会进行元素的深拷贝,突然间就开始想,能否实现一个通用的多维数组的深拷贝(仅使用JDK 7附带的类库)呢?于是就开始摸索了一下,似乎可以使用反射实现,但实用性可能不强…顺便提一下,处理非Cloneable对象的方式有些马虎(含糊)。
(2019/5/8 追記)
我发现这篇旧的垃圾文章竟然被提名在这里,所以从实现示例的评论中删除了“引用传递”的表达(汗)
通过反思来执行
import java.lang.reflect.Array;
import java.lang.reflect.Method;
public class ArrayUtils {
public static Object deepClone(Object original) {
// 引数がnullもしくは非配列の場合はnullを返す
if (original == null || !original.getClass().isArray()) {
return null;
}
// 同期化
synchronized (original) {
// 配列の要素の型
Class<?> componentType = original.getClass().getComponentType();
// 配列の要素の数
int length = Array.getLength(original);
// 配列の複製(ガラ)を生成
Object duplicate = Array.newInstance(componentType, length);
try {
// 配列の要素の数だけループ処理
for (int i = 0; i < length; i++) {
Object item = Array.get(original, i);
if (item == null) {
// 要素がnullの場合
Array.set(duplicate, i, null);
}
else {
if (item.getClass().isArray()) {
// 要素が配列の場合は再帰処理開始
Object clone = deepClone(item);
Array.set(duplicate, i, componentType.cast(clone));
}
else {
//要素が配列ではない場合
Class<?> type = item.getClass();
if (type.cast(item) instanceof Cloneable) {
// Cloneableインターフェイスを実装していればcloneメソッドの戻り値を格納
Method clone = type.getMethod("clone");
Array.set(duplicate, i, type.cast(clone.invoke(item)));
}
else {
// Cloneableインターフェイスを実装していなければそのまま格納(ディープコピーできてないかも)
Array.set(duplicate, i, type.cast(item));
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
return duplicate;
}
}
}
如果我们仅限制在可序列化的对象数组上,似乎可以使用以下实现。不过,性能可能会受到影响。
使用序列化和反序列化进行实现
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Array;
public class ArrayUtils {
public static Object deepClone(Object original) {
// 引数がnullもしくは非配列、シリアライズ不可の場合はnullを返す
if (original == null || !original.getClass().isArray() || !isSerializable(original)) {
return null;
}
// 同期化
synchronized(original) {
ByteArrayOutputStream baos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bais = null;
ObjectInputStream ois = null;
try {
// シリアライズ・デシリアライズ
baos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(baos);
oos.writeObject(original);
oos.flush();
bais = new ByteArrayInputStream(baos.toByteArray());
ois = new ObjectInputStream(bais);
return ois.readObject();
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
// クローズ処理
if (ois != null) try { ois.close(); } catch (IOException ioe) {};
if (bais != null) try { bais.close(); } catch (IOException ioe) {};
if (oos != null) try { oos.close(); } catch (IOException ioe) {};
if (baos != null) try { baos.close(); } catch (IOException ioe) {};
}
}
}
public static boolean isSerializable(Object target) {
// 引数がnullもしくは非配列の場合はfalseを返す
if (target == null || !target.getClass().isArray()) {
return false;
}
// 配列の要素の型
Class<?> componentType = target.getClass().getComponentType();
// 配列の先頭の要素
Object item = Array.get(target, 0);
if (componentType.isArray()) {
// 要素が配列の場合は再帰処理の結果を返す
return isSerializable(item);
}
else {
if (componentType.isPrimitive() || componentType.cast(item) instanceof Serializable) {
// シリアライズ可
return true;
}
else {
// シリアライズ不可
return false;
}
}
}
}