Java ArrayList 副本
Posted
技术标签:
【中文标题】Java ArrayList 副本【英文标题】:Java ArrayList copy 【发布时间】:2011-09-26 00:54:47 【问题描述】:我有一个大小为 10 的 ArrayList
l1
。我将 l1
分配给新的列表引用类型 l2
。 l1
和 l2
会指向同一个 ArrayList
对象吗?还是将ArrayList
对象的副本分配给l2
?
当使用l2
引用时,如果我更新列表对象,它也会反映l1
引用类型的变化。
例如:
List<Integer> l1 = new ArrayList<Integer>();
for (int i = 1; i <= 10; i++)
l1.add(i);
List l2 = l1;
l2.clear();
除了创建 2 个列表对象以及对集合进行从旧到新的复制之外,是否没有其他方法可以将列表对象的副本分配给新的引用变量?
【问题讨论】:
【参考方案1】:是的,赋值只会将l1
(这是一个引用)的值复制到l2
。它们都将引用同一个对象。
创建一个浅拷贝非常简单:
List<Integer> newList = new ArrayList<>(oldList);
(仅作为一个例子。)
【讨论】:
是否可以仅将数组列表的一部分复制到新的数组列表中,高效。例如:将位置 5 和 10 之间的元素从一个数组列表复制到另一个新数组列表。在我的应用程序中,范围会更大。 @Ashwin:这是一个 O(N) 操作,但是是的……您可以使用List.subList
来“查看”原始列表的一部分。
如果数组列表嵌套(ArrayList<ArrayList<Object>>)
怎么办?这会递归地创建所有子 ArrayList 对象的副本吗?
@Cat:不...这只是一个浅拷贝。
@ShanikaEdiriweera:你可以用流利的方式做到这一点,是的。但棘手的部分是创建一个 大多数 对象不会提供的深层副本。如果您有具体案例,我建议您提出一个包含详细信息的新问题。【参考方案2】:
尝试使用Collections.copy(destination, source);
【讨论】:
请解释一下为什么这可能比new ArrayList<>(source);
更可取?
@atc 这是另一种进行浅拷贝的方法,而不是 new ArrayList() 它使用了另一种算法,可用于任何 List 实现,而不仅仅是 ArrayList,仅此而已:)
这种方法很容易误导人!实际上,它的描述是。它说:“将元素从一个源列表复制到目标”,但它们没有被复制!它们被引用,所以只有 1 个对象的副本,如果它们是可变的,你就有麻烦了
在 java-api 中没有任何地方可以通过任何集合类进行深度克隆
这个答案没有多大意义。 Collections.copy
根本不是 new ArrayList<>(source)
的替代品。 Collections.copy
实际上所做的是假设destination.size()
至少和source.size()
一样大,然后使用set(int,E)
方法逐个索引地复制范围。该方法不会向目标添加新元素。 Refer to the source code if it's not clear enough from the Javadoc.【参考方案3】:
是的,l1
和 l2
将指向同一个引用,同一个对象。
如果你想基于另一个 ArrayList 创建一个新的 ArrayList,你可以这样做:
List<String> l1 = new ArrayList<String>();
l1.add("Hello");
l1.add("World");
List<String> l2 = new ArrayList<String>(l1); //A new arrayList.
l2.add("Everybody");
结果将是 l1
仍然有 2 个元素,l2
将有 3 个元素。
【讨论】:
你能解释一下List<String> l2 = new ArrayList<String>(l1)
和List<String> l2 = l1
之间的区别吗?
@MortalMan 不同之处在于 l2 = new ArrayList另一种将值从 src ArrayList 复制到 dest Arraylist 的便捷方法如下:
ArrayList<String> src = new ArrayList<String>();
src.add("test string1");
src.add("test string2");
ArrayList<String> dest= new ArrayList<String>();
dest.addAll(src);
这是对值的实际复制,而不仅仅是对引用的复制。
【讨论】:
我不完全确定这是准确的。我的测试显示相反(仍然引用同一个对象) 这个解决方案在使用 ArrayList 和 ArrayAdapter 时对我有用 这个答案是错误的。 addAll() 只是像 invertigo 所说的那样复制引用。这不是深拷贝。 对于 ArrayList有一个方法 addAll() 用于将一个 ArrayList 复制到另一个。
例如你有两个数组列表:sourceList和targetList,使用下面的代码。
targetList.addAll(sourceList);
【讨论】:
它也只是复制引用。【参考方案6】:Java 不传递对象,它传递对对象的引用(指针)。所以是的,l2 和 l1 是指向同一个对象的两个指针。
如果您需要两个具有相同内容的不同列表,则必须进行显式复制。
【讨论】:
如何制作“显式副本”?我猜你是在说深拷贝?【参考方案7】:
List.copyOf
➙ 不可修改的列表
你问:
有没有其他方法可以分配列表的副本
Java 9 带来了 List.of
方法,用于使用字面量创建未知具体类的不可修改的 List
。
LocalDate today = LocalDate.now( ZoneId.of( "Africa/Tunis" ) ) ;
List< LocalDate > dates = List.of(
today.minusDays( 1 ) , // Yesterday
today , // Today
today.plusDays( 1 ) // Tomorrow
);
除此之外,我们还收到了List.copyOf
。此方法也返回未知具体类的不可修改的List
。
List< String > colors = new ArrayList<>( 4 ) ; // Creates a modifiable `List`.
colors.add ( "AliceBlue" ) ;
colors.add ( "PapayaWhip" ) ;
colors.add ( "Chartreuse" ) ;
colors.add ( "DarkSlateGray" ) ;
List< String > masterColors = List.copyOf( colors ) ; // Creates an unmodifiable `List`.
“不可修改”是指列表中元素的数量,以及作为元素保存在每个槽中的对象引用是固定的。您不能添加、删除或替换元素。但是每个元素中保存的对象引用可能是也可能不是mutable。
colors.remove( 2 ) ; // SUCCEEDS.
masterColors.remove( 2 ) ; // FAIL - ERROR.
看到这个code run live at IdeOne.com。
dates.toString(): [2020-02-02, 2020-02-03, 2020-02-04]
colors.toString(): [AliceBlue, PapayaWhip, DarkSlateGray]
masterColors.toString(): [AliceBlue、PapayaWhip、Chartreuse、DarkSlateGray]
您询问了对象引用。正如其他人所说,如果您创建一个列表并将其分配给两个引用变量(指针),您仍然只有一个列表。两者都指向同一个列表。如果您使用任一指针修改列表,则两个指针稍后都会看到更改,因为内存中只有一个列表。
所以你需要制作一份清单。如果您希望该副本不可修改,请使用本答案中讨论的 List.copyOf
方法。在这种方法中,您最终会得到两个单独的列表,每个列表的元素都包含对相同内容对象的引用。例如,在我们上面的示例中,使用String
对象来表示颜色,颜色对象在内存中的某个地方浮动。这两个列表持有指向相同颜色对象的指针。这是一张图表。
第一个列表colors
是可修改的。这意味着可以删除一些元素,如上面的代码所示,我们删除了原始的第三个元素Chartreuse
(索引 2 = 序数 3)。并且可以添加元素。并且可以将元素更改为指向其他一些String
,例如OliveDrab
或CornflowerBlue
。
相比之下,masterColors
的四个元素是固定的。没有去除,没有添加,也没有替代另一种颜色。 List
的实现是不可修改的。
【讨论】:
【参考方案8】:只是为了完成: 上面的所有答案都是为了浅拷贝 - 保留原始对象的引用。我你想要一个深拷贝,你在列表中的(参考)类必须实现一个 clone/copy 方法,它提供单个对象的深拷贝。然后你可以使用:
newList.addAll(oldList.stream().map(s->s.clone()).collect(Collectors.toList()));
【讨论】:
以上是关于Java ArrayList 副本的主要内容,如果未能解决你的问题,请参考以下文章