如何防止一个 ArrayList 更改另一个 ArrayList

How can I prevent one ArrayList changing another

提问人:Gamaray 提问时间:9/1/2022 最后编辑:Alexander IvanchenkoGamaray 更新时间:9/1/2022 访问量:323

问:

我有两个相同的 s,比如 和 。ArrayListl1l2

例如,两者都是多维的。ArrayList<ArrayList<Integer>>

当我创建和填充时,我用来初始化 .如果这些是单个维度的,我的编辑将保持不变,但由于它们不是,因此添加新的 也会更改。但是,如果我要向它添加一个新的,则不会做任何事情。l1new ArrayList<>(l1);l2ArrayListl2l1Integerl2l1ArrayListl1

可以做些什么来确保更改元素不会篡改?l2l1

例:

ArrayList<ArrayList<Integer>> l1 = new ArrayList<>();

for (var x = 0; x < 10; x++) {
  l1.add(new ArrayList<>());

  for (var y = 0; y < 10; y++) {
    l1.get(x).add(y);
  }
}

ArrayList<ArrayList<Integer>> l2 = new ArrayList<ArrayList<Integer>>(l1);

//Should not change l1
l2.get(0).add(999);
Java ArrayList 引用 嵌套列表

评论


答:

1赞 Gatusko 9/1/2022 #1

创建这两个数组时。我们只创建了 ArrayList 的两个新对象,但没有在列表中创建对象的新实例。因此,如果 L1 进行更改,则这两个 ArrayLists 内部的任何内容都将反映更改,因为这两个 ArrayList 引用了内存中的同一对象。

为了使这项工作,不要像这样初始化

ArrayList<ArrayList<Integer>> l2 = new ArrayList<ArrayList<Integer>>(l1);

遍历 l1 并创建新对象。

评论

0赞 Gamaray 9/1/2022
谢谢,这确实有效,唯一的问题是该方法效率有点低(这将在神经网络的每个时代发生),但如果必须完成,则必须完成。再次感谢。
1赞 Alexander Ivanchenko 9/1/2022 #2

集合框架中的复制构造函数始终执行浅拷贝,而不是深拷贝,正如你所期望的那样。

ArrayList(Collection<? 扩展 E> c) 的 Javadoc 说:

构造一个列表,其中包含指定集合的元素,按集合的迭代器返回这些元素的顺序排列。

也就是说,生成的列表将包含完全相同的元素,而不是它们的副本。也就是说,在你的情况下,引用相同的内部列表。

因此,您需要手动复制嵌套列表中的每个元素:

public static <T> List<List<T>> copyNestedList(List<List<T>> list) {
    
    List<List<T>> copy = new ArrayList<>();
    for (List<T> l: list) {
        copy.add(new ArrayList<>(l));
    }
    return copy;
}

如果您对 Stream API 感到满意,您可以这样做:

public static <T> List<List<T>> copyNestedList(List<List<T>> list) {
    
    return list.stream()
        .map(ArrayList::new)
        .collect(Collectors.toList());
}

评论

0赞 Gamaray 9/1/2022
谢谢,是的,这就是我最终所做的。非示例数组是一个神经网络,因此它具有相当多的维度,但值得庆幸的是,循环似乎并没有过多地减慢优化速度。
0赞 Alexander Ivanchenko 9/1/2022
@Gamaray 没有干净而简单的方法来制作具有任意深度的嵌套的深层副本。就性能而言,普通循环是您可以使用的最佳工具。List
1赞 F. DePa 9/1/2022 #3

您可以手动创建一个新列表,而无需引用原始列表

ArrayList<ArrayList<Integer>> l1 = new ArrayList<>();

    for (var x = 0; x < 2; x++) {
      l1.add(new ArrayList<>());
    
      for (var y = 0; y < 2; y++) {
        l1.get(x).add(y);
      }
    }
    
    ArrayList<ArrayList<Integer>> l2 = new ArrayList<ArrayList<Integer>>();
    for (int i = 0;i < l1.size(); i++){
        
        ArrayList<Integer> tmp = new ArrayList<Integer>();
        
        for (int j = 0;j < l1.get(i).size(); j++){
            
            tmp.add(l1.get(i).get(j));
        }  
        l2.add(tmp);
    }