【问题标题】:Clone a List, Map or Set in Dart在 Dart 中克​​隆列表、地图或集合
【发布时间】:2014-03-11 18:07:36
【问题描述】:

来自 Java 背景:“克隆” Dart ListMapSet 的推荐方法是什么?

【问题讨论】:

标签: collections dart clone


【解决方案1】:

在 Java 中使用 clone() 是棘手且值得怀疑的1,2。实际上,clone() 是一个复制构造函数,为此,Dart 的 ListMapSet 类型都有一个名为 .from()named constructor,它们执行 shallow copy;例如鉴于这些声明

  Map<String, int> numMoons, moreMoons;
  numMoons = const <String,int>{ 'Mars' : 2, 'Jupiter' : 27 };
  List<String> planets, morePlanets;

你可以像这样使用.from()

  moreMoons = new Map<String,int>.from(numMoons)
    ..addAll({'Saturn' : 53 });
  planets = new List<String>.from(numMoons.keys);
  morePlanets = new List<String>.from(planets)
    ..add('Pluto');

请注意,List.from() 更普遍地接受迭代器,而不仅仅是 List

为了完整起见,我应该提到dart:html Node 类定义了一个clone() 方法。


1 J. Bloch,“Effective Java”第 2 版,第 11 条。
2B. Venners, "Josh Bloch on Design: Copy Constructor versus Cloning", 2002。从这里引用3。引用文章:

如果你读过我书中关于克隆的内容,特别是如果你读到字里行间,你就会知道我认为克隆被严重破坏了。 ---J.布洛赫

3Dart Issue #6459, clone instance(object).

【讨论】:

  • Josh Bloch 实际上参与了 Dart 集合 API 的一些早期设计。 Old interview.
  • .from() 和 .addAll() 并不是真正的克隆。他们在新的 Map/List/Set 中添加了一个引用。例如: Map map1 = { 'one': {'name': 1}, 'two': {'name': 2}, 'three': [{ 'a': { 'A': 1, 'B ': 2 }, 'b': { 'A': 3, 'B': 4 } }] };地图 map2 = new Map.from(map1); map2['两个']['名称'] = 4;改变map2['two']['name']后,map1也改变了
  • 对。 .from() 是一个 shallow 复制构造函数。为了清楚起见,我从未说过.from() 执行了克隆操作。我写的是clone() 是一种复制构造函数。
  • 请记住,如果您知道原始List 的类型,List&lt;E&gt;.of() 可能会更好。
【解决方案2】:

使用新版本的 dart 克隆地图或列表变得非常容易。 您可以尝试使用此方法对 List 和 Map 进行深度克隆。

对于列表

List a = ['x','y', 'z'];
List b = [...a];

用于地图

Map mapA = {"a":"b"};
Map mapB = {...mapA};

套装

Set setA = {1,2,3,};
Set setB = {...setA};

我希望有人觉得这很有帮助。

【讨论】:

  • @funder7 是的,这也适用于 Set。设置 set1 = {1,2,3,};设置 s = {...set1};
  • 但我尝试使用它,但出现错误:{...set1} 正在返回一个列表!我做错事情了?或者也许它是一个测试版功能?我用的是稳定频道
  • @funder7 当我在 dartpad 中运行相同的代码时,它运行良好。看这里imgur.com/JQkWZJS
【解决方案3】:

如果您使用的是 dart > 2.3.0,您可以使用扩展运算符,例如:

List<int> a = [1,2,3];
List<int> b = [...a]; // copy of a

【讨论】:

  • 请注意,这不会克隆对象列表。该列表将包含引用。
  • 这是浅拷贝
  • 不幸的是它会返回一个列表,所以它也不能与 Sets 一起使用
  • @funder 这是一个List,因为有方括号。一般技术适用于Sets 或Maps:{...set}{...map}
【解决方案4】:

对于列表和集合,我通常使用

List<String> clone = []..addAll(originalList);

正如@kzhdev 提到的,需要注意的是addAll()from()

[不要] 真的克隆。他们在新的 Map/List/Set 中添加了一个引用。

这对我来说通常没问题,但我会记住这一点。

【讨论】:

    【解决方案5】:

    对于深拷贝(克隆),您可以使用:

    Map<String, dynamic> src = {'a': 123, 'b': 456};
    Map<String, dynamic> copy = json.decode(json.encode(src));
    

    但可能对性能有些担忧。

    【讨论】:

    • 我不完全明白为什么这个回复被否决了,因为它确实有一些优势。是的,会有一些性能问题,但它确实会在里面复制列表,而不仅仅是复制指向它们的链接。所以我会给你一个赞成票
    • 只有当只有原始属性时,您的答案才有效。想象一下,你有 getter/setter/functions。 json.decode->encode 会破坏这一切,伙计
    • @qiAlex 这很明显,但这是一种有人可以选择的方式,伙计。很多时候,当您想要克隆和对象时,就是它包含原语的情况。克隆一个函数是什么意思!
    【解决方案6】:

    Map.from() 仅适用于一维地图。

    在dart中复制多维地图使用以下方法

    
        Map<keyType, valueType> copyDeepMap( Map<keyType, valueType> map )
        {
            Map<keyType, valueType> newMap = {};
    
            map.forEach
            (
                (key, value)
                {
                    newMap[key] =( value is Map ) ? copyDeepMap(value) : value ;
                }
            );
    
            return newMap;
        }
    
    

    【讨论】:

      【解决方案7】:

      这个解决方案应该有效:

        List list1 = [1,2,3,4]; 
      
        List list2 = list1.map((element)=>element).toList();
      

      它适用于列表,但应该适用于地图等,如果最后是列表,请记住添加到列表中

      【讨论】:

        【解决方案8】:

        对我来说最好的解决方案:

        List temp = {1,2,3,4}
        List platforms = json.decode(json.encode(parent.platforms));
        

        【讨论】:

        • 如果您在要复制的列表/地图/集中使用 DateTime,这将不起作用。
        【解决方案9】:

        这是我的解决方案。我希望它可以帮助某人。

          factory Product.deepCopy(Product productToCopy) => new Product(
            productToCopy.id,
            productToCopy.title,
            productToCopy.description,
            productToCopy.price,
            productToCopy.imageUrl,
            productToCopy.isFavorite,
          );}
        
        

        【讨论】:

          【解决方案10】:

          要复制 Map 过滤;

           var filteredNewCopy = filtered.map((key, value) => MapEntry(key, [...value]));
          

          【讨论】:

            【解决方案11】:

            没有 100% 防弹的方法来制作精确的独立副本,但 answer from Manish Dhruw 非常好。但是,它仅适用于包含简单变量类型和嵌套 Maps 的 Maps。

            要将其扩展为也适用于其他常见集合,例如ListSet,以及它们的组合,您可以使用类似于下面的代码。

            您实际上并不需要 DeepCopyable 类,但如果您想使用这些函数轻松地使自己的类“可深度复制”,它会很有用。

            abstract class DeepCopyable{
              T deepCopy<T>();
            }
            
            List<T> listDeepCopy<T>(List list){
              List<T> newList = List<T>();
            
              list.forEach((value) {
                newList.add(
                  value is Map ? mapDeepCopy(value) :
                  value is List ? listDeepCopy(value) :
                  value is Set ? setDeepCopy(value) :
                  value is DeepCopyable ? value.deepCopy() :
                  value
                );
              });
            
              return newList;
            }
            
            Set<T> setDeepCopy<T>(Set s){
              Set<T> newSet = Set<T>();
            
              s.forEach((value) {
                newSet.add(
                  value is Map ? mapDeepCopy(value) :
                  value is List ? listDeepCopy(value) :
                  value is Set ? setDeepCopy(value) :
                  value is DeepCopyable ? value.deepCopy() :
                  value
                );
              });
            
              return newSet;
            }
            
            
            Map<K,V> mapDeepCopy<K,V>(Map<K,V> map){
              Map<K,V> newMap = Map<K,V>();
            
              map.forEach((key, value){
                newMap[key] =
                  value is Map ? mapDeepCopy(value) :
                  value is List ? listDeepCopy(value) :
                  value is Set ? setDeepCopy(value) :
                  value is DeepCopyable ? value.deepCopy() :
                  value;
              });
            
              return newMap;
            }
            

            正如我所提到的,它显然仍然不是 100% 防弹 - 例如,您将丢失嵌套集合的类型信息。

            【讨论】:

              【解决方案12】:

              给出的答案很好,但请注意 generate 构造函数,如果您想“增长”一个固定长度的列表,它会很有帮助,例如:

              List<String> list = new List<String>(5);
              int depth = 0; // a variable to track what index we're using
              
              ...
              depth++;
              if (list.length <= depth) {
                list = new List<String>.generate(depth * 2,
                    (int index) => index < depth ? list[index] : null,
                    growable: false);
              }
              

              【讨论】:

                猜你喜欢
                • 2019-12-17
                • 2015-05-25
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2018-04-01
                • 2010-10-16
                相关资源
                最近更新 更多