【问题标题】:Copy contents of one List into another List (not reference)将一个列表的内容复制到另一个列表中(不是参考)
【发布时间】:2017-08-24 11:10:52
【问题描述】:

我有两个列表:

List<Object> listA = new ArrayList<Object>();
List<Object> newListB = new ArrayList<Object>();

如何将 listA 的内容复制到 newListB 中,例如,如果我更改了复制列表中的一个元素,我不想在原始列表中更改它。

【问题讨论】:

  • 这不是我想要的,因为如果我更改复制列表中的元素之一,它也会在我的原始列表中更改。
  • @Jens 这不是重复的。请删除标志。他不是在询问要克隆的 List。但他想在 List 中克隆对象。
  • @Dhiraj 是的,它没有重复,谢谢您的关注
  • @Xenu 我会尽力帮助您仍在使用 cmets。如果他移除旗帜,我会详细回答您。到目前为止,只需迭代 listA 并克隆每个元素并添加到 newListB

标签: java list collections


【解决方案1】:

你可以使用Java深度克隆的概念:

基本思路是这样的:

您有一个 Java 对象,并且您想要对它进行完整的克隆(副本)。 通过将您的类标记为Serializable,您可以将它们写为对象流,然后将它们作为不同的对象读回。 当您将对象作为不同的对象读回时,您很快就会得到原始对象的深层克隆。

Java 深度克隆方法

得到正确答案,以下方法将让您对 Java 对象进行深度克隆:

/**
 * This method makes a "deep clone" of any Java object it is given.
 */
 public static Object deepClone(Object object) {
   try {
     ByteArrayOutputStream baos = new ByteArrayOutputStream();
     ObjectOutputStream oos = new ObjectOutputStream(baos);
     oos.writeObject(object);
     ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
     ObjectInputStream ois = new ObjectInputStream(bais);
     return ois.readObject();
   }
   catch (Exception e) {
     e.printStackTrace();
     return null;
   }
 }

在下面的链接中找到更多详细信息:

A Java deep clone (deep copy) example

【讨论】:

  • 您可以将listA 传递给deepClone() 并将返回的对象分配给newListB(在转换之后)。
  • 序列化成本高
【解决方案2】:

试试这样的。

 List<Object> listA = new ArrayList<Object>();
    List<Object > newListB = new ArrayList<Object>();

    for (Object object : listA ) {
      newListB .add(object .clone());
    }

相同的工作示例如下

public static void main(String[] args){
        TestClass test = new TestClass(1);
        List<TestClass> listA = new ArrayList<TestClass>();
        listA.add(test);
            List<TestClass> newListB = new ArrayList<TestClass>();

            for (TestClass test1: listA ) {
                TestClass classSome = new TestClass();
                classSome = (TestClass)test1.clone();
              newListB.add(classSome );
            }

        System.out.println("listA id ="+listA.get(0).id);
        System.out.println("newListB id ="+newListB .get(0).id);

        newListB .get(0).setId(15);

        System.out.println("listA id ="+listA.get(0).id);
        System.out.println("newListB id ="+newListB .get(0).id);
    }

TestClass 如下所示

class TestClass implements Cloneable{
        int id;
        public TestClass(){

        }
        public TestClass(int id){
            this.id = id;   
        }

        public int getId(){
            return this.id;
        }

        public void setId(int id){
             this.id=id;
        }

        @Override
         public Object clone() {
                try {
                     return (TestClass)super.clone();
                 }
                 catch (CloneNotSupportedException e) {
                 return null;
                    // This should never happen
                 }
            }
    }

【讨论】:

  • 这个想法很好,只要对象是可克隆的,但你需要在克隆之前将每个对象转换为Cloneable
  • @OleV.V.不,你没有。你不需要投射任何东西。您需要实施 Cloneable 并遵循其相当复杂的规则。写一个拷贝构造函数会好很多。
  • java中的clone()方法坏了
  • 它是一个 java api。您可以创建自己的方式来执行此操作。但是,如果您实施其他方式来做到这一点。请在回答这个问题时提供。
  • @OleV.V.第一个 sn-p 只是一个例子。但是我又给出了两个 sn-p,这是对此的实际实现。我的回答更具体到 List 是具有固定类型的对象。对于具有不同类型的对象列表,我还没有考虑过。我也很好奇如何将其应用于通用 List 。
【解决方案3】:

不敢告诉你克隆在java中被破坏了:

  • https://en.wikipedia.org/wiki/Clone_(Java_method)
  • http://www.javapractices.com/topic/TopicAction.do?Id=71

    所以你最好需要实现你的深拷贝方法(这会给你带来 O(log n) 问题)

    List<Point> listA = new ArrayList<>();
    listA.add(new Point(0, 0));
    listA.add(new Point(1, 1));
    listA.add(new Point(2, 2));
    
    List<Point> newListB = new ArrayList<>();
    
    for (Point point : listA) {
        newListB.add(new Point(point));
    }
    System.out.println("Before deep copy");
    System.out.println(listA);
    System.out.println(newListB);
    listA.get(0).setX(987);
    listA.get(0).setY(987);
    System.out.println("after changes ");
    System.out.println(listA);
    System.out.println(newListB);
    

【讨论】:

    【解决方案4】:
    只需使用构造函数 public ArrayList(Collection c) 通过传递第一个来构造第二个列表。它按照迭代器给定的顺序使用第一个列表中的所有项目初始化您的列表。

    您可以使用反射将一个列表的所有元素深度复制到另一个列表。请参阅下面的示例代码,它可以扩展以满足您的需求、更多(更深入)的案例等。

    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Objects;
    
    public class Main {
    
        public static void main(String[] args) {
    
            List<Object> listA = new ArrayList<>();
    
            listA.add(false);
            listA.add(123);
            listA.add("test");
            listA.add(new Foo(1, "foo", 7, new Bar(2)));
    
            System.out.println("==ListA==");
            listA.forEach(x -> System.out.println(x));
    
            List<Object> listB = new ArrayList<>(listA.size());
    
            for (Object obj : listA) {
                try {
                    Object o = null;
                    if (isPrimitiveWrapperClassOrString(obj.getClass())) {
                        o = newInstance(obj);
                    } else {
                        o = obj.getClass().newInstance();
                        copyValues(obj, o);
                    }
                    listB.add(o);
                } catch (NoSuchMethodException e) {
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
    
            System.out.println("\n==ListB==");
            listB.forEach(x -> System.out.println(x));
    
            // Modify listB
            listB.set(0, true);
            listB.set(1, 456);
            ((Foo)listB.get(3)).setId(2);
            ((Bar)((Foo)listB.get(3)).getBar()).setId(9);
    
            System.out.println("\n==ListA after modifying listB==");
            listA.forEach(x -> System.out.println(x));
    
            System.out.println("\n==ListB==");
            listB.forEach(x -> System.out.println(x));
        }
    
        private static Object newInstance(Object obj) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
            Class classType = obj.getClass();
            Object o = null;
    
            if (classType == Boolean.class) {
                Method method = classType.getDeclaredMethod("booleanValue", null);
                boolean value = (boolean) method.invoke(obj, null);
                o = classType.getConstructor(boolean.class).newInstance(value);
            } else if (classType == Byte.class) {
                Method method = classType.getDeclaredMethod("byteValue", null);
                byte value = (byte) method.invoke(obj, null);
                o = classType.getConstructor(byte.class).newInstance(value);
            } else if (classType == Short.class) {
                Method method = classType.getDeclaredMethod("shortValue", null);
                short value = (short) method.invoke(obj, null);
                o = classType.getConstructor(short.class).newInstance(value);
            } else if (classType == Integer.class) {
                Method method = classType.getDeclaredMethod("intValue", null);
                int value = (int) method.invoke(obj, null);
                o = classType.getConstructor(int.class).newInstance(value);
            } else if (classType == Long.class) {
                Method method = classType.getDeclaredMethod("longValue", null);
                long value = (long) method.invoke(obj, null);
                o = classType.getConstructor(long.class).newInstance(value);
            } else if (classType == Float.class) {
                Method method = classType.getDeclaredMethod("floatValue", null);
                float value = (float) method.invoke(obj, null);
                o = classType.getConstructor(float.class).newInstance(value);
            } else if (classType == Double.class) {
                Method method = classType.getDeclaredMethod("doubleValue", null);
                double value = (double) method.invoke(obj, null);
                o = classType.getConstructor(double.class).newInstance(value);
            } else if (classType == Character.class) {
                Method method = classType.getDeclaredMethod("charValue", null);
                char value = (char) method.invoke(obj, null);
                o = classType.getConstructor(char.class).newInstance(value);
            } else if (classType == String.class) {
                Method method = classType.getDeclaredMethod("toString", null);
                String value = (String) method.invoke(obj, null);
                o = classType.getConstructor(String.class).newInstance(value);
            }
    
            return o;
        }
    
        private static void copyValues(Object objF, Object objT) throws IllegalAccessException, InstantiationException {
            Class classType = objF.getClass();
            for (Field field : classType.getDeclaredFields()) {
                field.setAccessible(true);
                Class fieldType = field.getType();
                if (isPrimitiveWrapperClassOrString(fieldType)) {
                    field.set(objT, field.get(objF));
                } else {
                    Object objN = field.get(objT);
                    if (Objects.isNull(objN)) objN = field.getType().newInstance();
                    copyValues(field.get(objF), objN);
                    field.set(objT, objN);
                }
            }
        }
    
        private static boolean isPrimitiveWrapperClassOrString(Class classType) {
            return classType == Boolean.class || classType == boolean.class ||
                    classType == Byte.class || classType == byte.class ||
                    classType == Short.class || classType == short.class ||
                    classType == Integer.class || classType == int.class ||
                    classType == Long.class || classType == long.class ||
                    classType == Float.class || classType == float.class ||
                    classType == Double.class || classType == double.class ||
                    classType == Character.class || classType == char.class ||
                    classType == String.class;
        }
    }
    
    class Foo {
        private int id;
        private String label;
        private Integer stock;
        private Bar bar;
    
        public Foo() { }
        public Foo(int id, String label, Integer stock, Bar bar) {
            this.id = id;
            this.label = label;
            this.stock = stock;
            this.bar = bar;
        }
    
        public int getId() { return this.id; }
        public void setId(int id) { this.id = id; }
        public String getLabel() { return this.label; }
        public void setLabel() { this.label = label; }
        public Integer getStock() { return this.stock; }
        public void setStock(Integer stock) { this.stock = stock; }
        public Bar getBar() { return this.bar; }
        public void setBar(Bar bar) { this.bar = bar; }
    
        @Override
        public String toString() {
            return String.format("%s | %d | %d | %s", this.label, this.id, this.stock, this.bar);
        }
    }
    
    class Bar {
        private int id;
    
        public Bar() {}
        public Bar(int id) { this.id = id; }
    
        public int getId() { return this.id; }
        public void setId(int id) { this.id = id; }
    
        @Override
        public String toString() { return "Bar: " + this.id; }
    }
    

    哪些输出

    ==ListA==
    false
    123
    test
    foo | 1 | 7 | Bar: 2
    
    ==ListB==
    false
    123
    test
    foo | 1 | 7 | Bar: 2
    
    ==ListA after modifying listB==
    false
    123
    test
    foo | 1 | 7 | Bar: 2
    
    ==ListB==
    true
    456
    test
    foo | 2 | 7 | Bar: 9
    

    【讨论】:

    • 这复制了参考文献,与提问者的要求相反
    • 我已经编辑了我的答案,因为我后来明白你需要深拷贝
    猜你喜欢
    • 2018-08-11
    • 2022-11-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-07-28
    • 2017-11-02
    • 1970-01-01
    • 2015-08-09
    相关资源
    最近更新 更多