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

Posted

技术标签:

【中文标题】如何在不影响 Flutter 中的原始列表的情况下将列表执行深层复制到另一个列表【英文标题】:How to perform deep copy for a list to another list without affecting the original one in Flutter 【发布时间】:2021-11-29 12:07:54 【问题描述】:

我有两个 Type 对象列表,_types_filteredTypes

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

一个 Type 对象是:

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

问题是当我需要获取原始列表时,我得到了主要的 typetype.subTypes 仍然是根据先前的搜索而不是原始搜索过滤的!即使它被复制而没有引用_filteredTypes = List.from(_types);

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

【问题讨论】:

您能否进一步澄清这个问题?你是在问为什么_types在你改变_filteredTypes时会改变? 是的,当然,我需要知道为什么在过滤 _filteredTypes.subType 时 _types.subType 会发生变化 【参考方案1】:

这是对具有子列表的列表执行深层复制的方法,谢谢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

【讨论】:

【参考方案2】:

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;

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

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

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

【讨论】:

感谢您的详细信息,它有很大帮助,但我想知道如何,仍然找不到执行列表深拷贝的方法!

以上是关于如何在不影响 Flutter 中的原始列表的情况下将列表执行深层复制到另一个列表的主要内容,如果未能解决你的问题,请参考以下文章

如何编写一个函数,在不使用返回的情况下更改原始整数列表?

如何在不影响原始 bean 的“客户”的情况下声明另一个 Jackson ObjectMapper?

(Flutter) 如何在不按“完成”的情况下自动监听 TextField 中的变化?

如何在不更改原始数组的情况下更改函数中的数组? [复制]

颤振:如何在不按按钮的情况下进行列表视图更新?

Flutter - 如何在不阻止UI的情况下计算包含未来的繁重任务?