Dart中如何无引用复制多维List 解决浅拷贝修改影响原列表问题
Dart二维List浅拷贝导致原数据被修改的解决方法
问题复现
编写的矩阵行列式计算Dart代码如下:
double determinant(List<dynamic> a) { print(a); int n = a.length; List<dynamic> am = a.map((dynamic e) => e).toList(); for (int fd = 0; fd < n; fd++) { for (int i = fd + 1; i < n; i++) { if (am[fd][fd] == 0) { am[fd][fd] = 1.0e-18; } var crScaler = am[i][fd] / am[fd][fd]; for (int j = 0; j < n; j++) { am[i][j] -= crScaler * am[fd][j]; } } } double product = 1.0; for (int i = 0; i < n; i++) { product *= am[i][i]; } print(a); return product; }
使用如下测试用例调用:
final List<dynamic> a = [ [2.0, 3.0], [1.0, 6.0] ]; determinant(a);
运行后原入参a被意外修改,输出如下:
[[2.0, 3.0], [1.0, 6.0]] [[2.0, 3.0], [0.0, 4.5]]
原因分析
判断完全正确:a.map((dynamic e) => e).toList() 仅实现了第一层浅拷贝:
- 生成的新外层列表
am是独立对象 - 但
am中存储的每一个子列表(矩阵的每一行),仍然是原入参a中对应子列表的引用
后续高斯消元过程中修改am子列表内的元素时,本质是直接修改原列表a的内部元素,导致原数据被污染。
解决方案
针对二维数值矩阵的场景,只需要在拷贝外层列表的同时,对每一行的子列表也做一次独立复制即可,替换原代码中创建am的行:
// 对每一行子列表单独做复制,实现二维列表的完全独立拷贝 List<dynamic> am = a.map((dynamic row) => List<dynamic>.from(row)).toList();
List.from(row) 会基于当前行的元素生成一个全新的列表对象,和原行没有引用关联,后续修改am的任意元素都不会影响原入参a。
注意:该方法仅适用于子列表元素为数值、字符串等基本类型的二维列表,和当前计算行列式的使用场景完全匹配;如果列表内嵌套了其他自定义引用类型对象,需要对应层级继续执行拷贝逻辑。
修正后重新运行测试用例,函数首尾两次打印的原列表a值完全一致,不会再被修改。
如果需要处理嵌套层级不固定的通用纯数据列表深拷贝,可以通过递归遍历的方式实现,但固定二维结构的矩阵用上述逐行拷贝的方式性能最优,没有额外递归开销。
内容的提问来源于stack exchange,提问作者haitham
相关产品推荐
相关产品推荐

