如何在 Java 中制作 ArrayList<Integer> 的深层副本? [复制]

Posted

技术标签:

【中文标题】如何在 Java 中制作 ArrayList<Integer> 的深层副本? [复制]【英文标题】:How do I make a deep copy of an ArrayList<Integer> in Java? [duplicate] 【发布时间】:2017-06-23 08:29:06 【问题描述】:

我基本上只是想对 1 和 0 进行深度复制,所以我可以使用布尔值,但我想知道如何在一般情况下为整数执行此操作。

private ArrayList<Integer> makeDeepCopyInteger(ArrayList<Integer> a) 
    ArrayList<Integer> newA = new ArrayList<>(a.size());
    for (int i = 0; i < a.size(); i++) 
        int newInt = 0;
        if (a.get(i) == 1) 
            newInt = 1;
        
        newA.add(newInt);
    
    return newA;

【问题讨论】:

既然整数是不可变的,为什么要深拷贝它们? 【参考方案1】:

clone() 方法受Integer 类的保护,因此您不能在该类之外调用Integer.clone()。您可以做的是创建一个 new 整数。

private ArrayList<Integer> makeDeepCopyInteger(ArrayList<Integer> old)
    ArrayList<Integer> copy = new ArrayList<Integer>(old.size());
    for(Integer i : old)
        copy.add(new Integer(i));
    
    return copy;

您可以通过执行以下操作来测试它是否有效:

public static void main (String[] args) throws java.lang.Exception

    ArrayList<Integer> arr = new ArrayList<>();
    for(int i = 0; i<5; i++)
        arr.add(new Integer(i));
    
    ArrayList<Integer> x = makeDeepCopyInteger(arr);
    for(int i = 0; i<x.size(); i++)
        if(arr.get(i) == x.get(i))
            System.out.println("Same object");
         else 
            System.out.println("Not the same object");
        
    


测试

Integer a = new Integer(1);
Integer b = new Integer(a);

System.out.println(a==b); // true
System.out.println(System.identityHashCode(a) == (System.identityHashCode(b))); // false;


Integer a = new Integer(1);
Integer b = a;

System.out.println(a==b); // true
System.out.println(System.identityHashCode(a) == (System.identityHashCode(b))); // true

所以从我的测试看来,要创建一个用于复制到新数组的新引用,您应该使用new Integer()Integer 是一个不可变对象,但当 Integer 的值发生变化时,该引用也会发生变化。

【讨论】:

由于Integer 是不可变的,您不需要创建它的副本。 @Kayaman 我对此进行了一些测试,请参阅我的编辑。也许我很困惑,但由于不创建副本,它似乎保留了旧的参考。 这没有什么问题。由于Integer 的值不能改变,例如,如果您有1000 个列表都引用相同的17 并不重要。你的最后一段是错误的。由于Integer 是不可变的,所以它的值不能改变。【参考方案2】:

使用流来复制对象。易于阅读,适合 JIT。以下代码提供了一个列表副本,其中包含 Integer 对象副本。

private ArrayList<Integer> makeDeepCopyInteger(ArrayList<Integer> a)
    return a.stream().map(val -> new Integer(val)).collect(toList());

复制除整数以外的自定义对象覆盖实现并调用clone()

return a.stream().map(MyObjectClass::clone).collect(toList());

您可以使用序列化到 json 来代替克隆。例如。在 BeanUtils.getCopy(sourceBean) 中使用 java-utils

【讨论】:

欢迎来到堆栈溢出 :-) 请看How to Answer。您应该提供一些信息为什么您的代码可以解决问题。仅代码答案对社区没有用处。【参考方案3】:

可以执行以下操作:

public static List<Integer> clone(List<Integer> source) 
   return source.stream()
       .map( intObj -> new Integer(intObj.intValue()))
       .collect(Collectors.toList());

或者,更老式的:

public static List<Integer> clone(List<Integer> source) 
   List<Integer> newList = new ArrayList<>();
   for(Integer intObj : source) 
      newList.add(new Integer(intObj.intValue()));
   
   return newList;

通过利用自动装箱/自动拆箱,可以缩短这两者。但我已经明确表示要绝对清楚发生了什么。

但是,这是一种毫无意义的练习 - 事实上,它会浪费内存并且不利于性能。 Integer 是不可变的,因此更好的引用指向Integer 的同一实例。因为Integer不可能改变值,所以不可能通过共享实例造成任何伤害。

这适用于一般的不可变对象,这也是它们是好东西的原因。

作为初学者,您不太可能找到new Integer(...) 是一个好主意的情况(甚至Integer.valueOf(int i),尽管这可能会返回一个缓存实例)。如果您已经有Integer,请使用您拥有的:

 Integer oldVar = ... ;
 Integer newVar = oldVar;

不变性意味着永远没问题。对newVar 的操作不可能破坏oldVar,因为没有newVar.setValue(newValue)

如果您有int,请直接使用它并允许Java 的自动装箱将其转换为Integer

 int oldValue = ... ;
 Integer newValue = oldValue ; // Java will automatically put this through
                               // Integer.valueOf(int i)

您提到您真的很想使用布尔值。您应该考虑使用BitSet

【讨论】:

【参考方案4】:

您必须遍历列表中的项目并在之前克隆它们,然后将它们添加到新列表中,如下所述:

How to clone ArrayList and also clone its contents?

【讨论】:

以上是关于如何在 Java 中制作 ArrayList<Integer> 的深层副本? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

如何制作Java ArrayList的深层副本[重复]

如何从java arraylist中删除重复的对象[重复]

在 Java 中不使用 Arraylist 的情况下为类制作动态数组,并且没有正确教授如何去做

java中,如何在二维的ArrayList中添加List

如何在jsp中显示java中的ArrayList项?

如何在 Java 中正确返回 ArrayList 的一部分?