如何在不影响 Flutter 中的原始列表的情况下将列表深复制到另一个列表

How to perform deep copy for a list to another list without affecting the original one in Flutter

我有两个 Type 对象列表,_types_filteredTypes:

List<Type> _types = []; // Original list
List<Type> _filteredTypes = []; // Where i bind filter the contents

类型对象是:

class Type extends Equatable {
  final int id;
  final String title;
  List<SubType>? subTypes;

  Type({
    required this.id,
    required this.title,
    this.subTypes,
  });
}

一个 SubType 对象是:

class SubType extends Equatable {
  final int id;
  final String title;

  Type({
    required this.id,
    required this.title,
  });
}

我需要根据搜索文本过滤列表,因此只要用户输入字母,_filteredTypes 就会更新。

我在 setState() 中对 _onSearchChangedEvent() 进行筛选,如下所示:

_filteredTypes = List.from(_types); // To get the original list without filter

for(var i = 0; i < _filteredTypes.length; i++) {
    // This is where i filter the search result when a subType.title matches the search query:
    _filteredTypes[i].subTypes = List.from(_types[i].subTypes!.where((element) => element.title.toLowerCase().contains(query!.toLowerCase())).toList());
}

// This is where i filter the search result and remove any type doesn't match any subType.title:
_filteredTypes.removeWhere((element) => element.subTypes!.length == 0);

bindTypes(); // To refresh the list widget

问题是当我需要获取原始列表时,我得到了主要的 type 但是 type.subTypes 仍然是基于过滤的以前的搜索不是原来的!即使是在没有引用的情况下复制 _filteredTypes = List.from(_types);

这似乎是 Flutter 中的一个错误,伙计们有什么想法吗?

List.from 不会为您提供 _types 的深拷贝 - 它会为您提供浅拷贝。这意味着 _filteredTypes_types 共享相同的 subTypes。它的行为类似于此示例

var sharedList = [1];
final listOne = [1, sharedList];
final listTwo = [1, sharedList];
sharedList[0]  = 2;

更改 sharedList 将同时更改 listOnelistTwo 中的值。如果共享列表只是一个整数,则更改该整数不会产生相同的效果。就像这个例子:

var sharedInteger = 1;
final listOne = [1, sharedInteger];
final listTwo = [1, sharedInteger];
sharedInteger  = 2;

当你创建一个 class 的实例时,它可能像 List 或你自己的自定义 class 一样内置,你得到的返回是一个引用或指向它的指针instance/object。对象本身分配在堆内存区域而不是堆栈上,这意味着可以在函数外部引用该对象。因为它的生命(对象生命)不受函数范围的限制,所以当你到达函数的末尾 } 时,对象仍然存在,并且它的内存被称为垃圾收集器的特殊程序释放。

与许多现代编程语言一样,在 dart 中使用垃圾收集器并在堆上自动分配对象。例如,在 C++ 等语言中,您可以在堆栈上分配对象,并且必须明确堆分配,并在完成后释放堆上的任何对象。

上面的所有内容你都可以查看它的要点是因为 subtypes 是一个列表,它是一个引用类型所以 _filteredTypes_types 都有那个引用。如果你想要一个深拷贝,你也可以这样做,我会留给你看。

这是对包含子列表的列表执行深度复制的方法,谢谢moneer alhashim,您的回答指导了我。

我将其作为答案发布,以帮助其他人轻松找到解决方案。

所以,这里的关键是映射原始列表 types.map(...) 并手动填充它而不是使用 List.from(),这将为深层对象创建一个新实例。

首先,我为每个列表声明了一个函数:

//For the parent list:

List<Types> getNewTypesInstance {
    // This function allows to perform a deep copy for the list.
    return types.map((e) =>
        Type(id: e.id, title: e.title, subTypes: e.subTypes))
        .toList();
}


// For the child list:

List<SubType> getNewSubTypesInstance(List<SubType> lst) {
    // This function allows to perform a deep copy for the list:
    return lst.map((e) =>
        SubType(id: e.id, title: e.title))
        .toList();
}

如果你有更深的列表,你将需要第三个函数来获取它作为新实例等等。

最后,调用方法是在setState()中写这段代码:

_filteredTypes = getNewTypesInstance

for(var i = 0; i < _filteredTypes.length; i++) {
    // This is where i filter the search result when a subType.title matches the search query:
    _filteredTypes[i].subTypes = List.from(getNewSubTypesInstance(_types[i].subTypes!.where((element) => element.title.toLowerCase().contains(query!.toLowerCase())).toList()));
}

// This is where i filter the search result and remove any type doesn't match any subType.title:
_filteredTypes.removeWhere((element) => element.subTypes!.length == 0);

bindTypes(); // To refresh the list widget