【问题标题】:How do I clone a generic List in Java?如何在 Java 中克隆通用列表?
【发布时间】:2010-09-08 11:14:04
【问题描述】:

我有一个ArrayList<String>,我想返回一份副本。 ArrayList 有一个具有以下签名的克隆方法:

public Object clone()

调用此方法后,如何将返回的Object强制转换回ArrayList<String>

【问题讨论】:

  • 不,这是一个有效的问题。 Java 不支持“真正的”泛型、运行时类型擦除等等,因此这些细节可能很棘手。此外,Cloneable 接口和 Object.clone() 方法机制同样令人困惑。
  • 好吧,我主要做 C#,这很容易。如果您希望我从这个问题中删除 cmets,请告诉我。
  • 您可以留下评论。我认为我的编辑解释了我遇到的问题。
  • 你的评论是可以的,如果有点居高临下的话。我认为 Java 开发人员必须跳过的许多障碍对 .NET 开发人员来说似乎很愚蠢。
  • @Oscar,他想克隆,而不是调用克隆命令。它可能不一样,因为克隆并没有真正克隆。我认为这就是重点。这确实是一个棘手的问题。

标签: java generics arraylist clone


【解决方案1】:

为什么要克隆?创建一个新列表通常更有意义。

List<String> strs;
...
List<String> newStrs = new ArrayList<>(strs);

工作完成。

【讨论】:

  • 你可能不知道它是什么类型的List。它可能是 LinkedList、MyOwnCustomList 或 ArrayList 的子类,在这种情况下,新建 ArrayList 将是不正确的类型。
  • 我会关心原始列表使用的是哪个实现吗?我可能关心新列表使用哪种实现。
  • @Steve Kuo:签名是ArrayList(Collection&lt;? extends E&gt; c),这意味着你使用什么样的列表作为参数并不重要。
  • 我的意思是它不是深拷贝,是吗?
  • @YekhezkelYovel 不,不会。
【解决方案2】:
ArrayList newArrayList = (ArrayList) oldArrayList.clone();

【讨论】:

  • 这适用于字符串(这是问题所要求的),但值得注意的是 ArrayList.clone 将执行浅拷贝,因此如果列表中有可变对象,它们不会被克隆(并且更改一个列表中的一个也会更改另一个列表中的那个。
  • 您应该避免在遗留代码之外的任何地方使用原始类型。你最好使用ArrayList&lt;String&gt; newArrayList = (ArrayList&lt;String&gt;) oldArrayList.clone();
  • 太糟糕了 ArrayList 有一个#clone 方法,但 List 本身没有。叹息。
  • 不相关,但同时使用:在左侧使用List&lt;String&gt; 而不是ArrayList&lt;String&gt;。这就是大多数时候应该使用集合的方式。
  • 我无法使用此方法复制 ArrayList。无法识别 clone() 方法。
【解决方案3】:

这是我使用的代码:

ArrayList copy = new ArrayList (original.size());
Collections.copy(copy, original);

希望对你有用

【讨论】:

  • 并避免使用原始类型.. 所以而不是ArrayList 使用ArrayList&lt;YourObject&gt;
  • docs.oracle.com/javase/8/docs/api/java/util/… 不起作用,因为列表大小不同。
  • 为什么不直接使用ArrayList构造函数的复制重载?
【解决方案4】:

请注意,Object.clone() 存在一些重大问题,并且在大多数情况下不鼓励使用它。请参阅 Joshua Bloch 的“Effective Java”中的第 11 项以获得完整答案。我相信您可以安全地在原始类型数组上使用 Object.clone(),但除此之外,您需要谨慎地正确使用和覆盖克隆。您最好定义一个复制构造函数或静态工厂方法,根据您的语义显式克隆对象。

【讨论】:

    【解决方案5】:

    在 Java 8 中,它可以通过流进行克隆。

    import static java.util.stream.Collectors.toList;
    

    ...

    List<AnObject> clone = myList.stream().collect(toList());
    

    【讨论】:

    • 可以像 List xy = new ArrayList(oldList);
    • 这不是深拷贝,一个列表元素的变化可以在另一个列表中看到
    • 问题在哪里指定需要深拷贝?假设需要另一个包含相同对象的集合。如果你想走深拷贝路线,那么你正在开辟一个全新的蠕虫罐头。
    • 虽然不是真正的克隆,是吗?来自 API 文档“无法保证返回的列表的类型、可变性、可序列化性或线程安全性”。 Euw,不想要其中之一。 toCollection 可能是更好的选择。
    • 为什么这个答案有这么多赞?!问题明确要求克隆,即深拷贝,请对此答案投反对票!
    【解决方案6】:

    我认为这应该使用 Collections API 来解决问题:

    注意:复制方法以线性时间运行。

    //assume oldList exists and has data in it.
    List<String> newList = new ArrayList<String>();
    Collections.copy(newList, oldList);
    

    【讨论】:

    • 为什么不直接使用 new ArrayList(oldList) ?
    • 我相信这行不通,来自 doc * 目标列表必须至少与源列表一样长。如果更长,则目标列表中的其余元素不受影响。
    • @GregDomjan 并不是说​​我同意这是最好的方法,但这是一种方法。要解决您的问题,它就像这样简单: List newList = new ArrayList(oldList.size());
    • 不确定它是否与Java 8有关,但即使指定大小,仍然会出现异常 IndexOutOfBoundsException: destination.size() 在List&lt;MySerializableObject&gt; copyList = new ArrayList&lt;&gt;(mMySerializableObjects.size()); 上似乎使用copyList.addAll(original); 是一个不错的选择
    • @GeneBo 它没有指定大小。它指定了容量,这是一个非常不同的事情。神秘的喜悦int 论点。我不知道你为什么要使用这种晦涩的静态方法而不是旧的 addAll
    【解决方案7】:

    我发现使用 addAll 效果很好。

    ArrayList<String> copy = new ArrayList<String>();
    copy.addAll(original);
    

    使用括号而不是泛型语法

    【讨论】:

    • 这适用于字符串,但不适用于可变对象。您也需要克隆它们。
    • 是的,他的问题是关于字符串的。而且他有泛型的问题,在这种情况下不太喜欢铸造的东西。
    • 另外,ArrayList.clone 只会进行浅层克隆,因此列表中的可变对象也不会使用该方法进行克隆。
    • 那应该是 ArrayList 顺便说一句。此外,您最好使用 new ArrayList(original) ,因为它的书写更少且同样清晰。
    • 为什么不直接使用new ArrayList&lt;String&gt;(original)
    【解决方案8】:

    如果您希望这样做以便能够在 getter 中返回 List,最好这样做:

    ImmutableList.copyOf(list);
    

    【讨论】:

      【解决方案9】:
      List<String> shallowClonedList = new ArrayList<>(listOfStrings);
      

      请记住,这只是浅拷贝而不是深拷贝,即。您会得到一个新列表,但条目是相同的。这对于简单的字符串来说没有问题。当列表条目本身是对象时,会变得更加棘手。

      【讨论】:

        【解决方案10】:

        要克隆像java.util.List 这样的通用接口,您只需要强制转换它。这是一个例子:

        List list = new ArrayList();
        List list2 = ((List) ( (ArrayList) list).clone());
        

        这有点棘手,但它可以工作,如果你被限制返回一个List 接口,那么你之后的任何人都可以随时实现你的列表。

        我知道这个答案接近最终答案,但我的答案回答了当您使用 List - 通用父级 - 而不是 ArrayList 时如何做所有这些事情

        【讨论】:

        • 你假设它总是一个不正确的 ArrayList
        • @JuanCarlosDiaz 会,这是问的问题,它是关于 ArrayList,所以我回答了 ArrayList :)
        【解决方案11】:

        克隆 ArrayList 时要非常小心。 java中的克隆很浅。这意味着它只会克隆 Arraylist 本身而不是其成员。因此,如果您有一个 ArrayList X1 并将其克隆到 X2 中,则 X2 中的任何更改也将体现在 X1 中,反之亦然。克隆时,您只会生成一个新的 ArrayList,其中包含指向原始元素中相同元素的指针。

        【讨论】:

          【解决方案12】:

          这也应该有效:

          ArrayList<String> orig = new ArrayList<String>();
          ArrayList<String> copy = (ArrayList<String>) orig.clone()
          

          【讨论】:

            【解决方案13】:
            ArrayList first = new ArrayList ();
            ArrayList copy = (ArrayList) first.clone ();
            

            【讨论】:

              【解决方案14】:

              我不是java专业的,但我也有同样的问题,我尝试通过这种方法解决。 (假设 T 有一个拷贝构造函数)。

               public static <T extends Object> List<T> clone(List<T> list) {
                    try {
                         List<T> c = list.getClass().newInstance();
                         for(T t: list) {
                           T copy = (T) t.getClass().getDeclaredConstructor(t.getclass()).newInstance(t);
                           c.add(copy);
                         }
                         return c;
                    } catch(Exception e) {
                         throw new RuntimeException("List cloning unsupported",e);
                    }
              }
              

              【讨论】:

              • 不能保证List 实现类有一个公共的无参数构造函数。例如,Lists 通过Array.asListList.ofList.subList 返回可能不会。
              猜你喜欢
              • 2010-09-18
              • 2017-04-11
              • 2011-02-12
              • 2012-02-13
              • 1970-01-01
              • 2012-09-30
              • 2017-09-17
              • 1970-01-01
              • 2014-05-23
              相关资源
              最近更新 更多