You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter-Dart中如何创建嵌套动态Map的完全独立副本?

Flutter/Dart 中动态嵌套Map/List的深拷贝方案

我在Flutter-Dart项目中遇到一个问题:服务器返回了一个包含多层嵌套Map和List的动态数据myMap,结构和长度都无法预先确定,示例如下:

Map<String, dynamic> myMap = {
  'key': {
    'key': [1, 2, 3],
    'key2': {
      'key': 23,
      'subKey': {
        'nestedKey': [
          4,
          5,
          {
            'deepKey': {
              'finalList': [
                9,
                10,
                {
                  'ultraKey': [11, 12]
                }
              ]
            }
          }
        ]
      }
    }
  },
  'key2': {
    'key': [
      1,
      2,
      3,
      {
        'dsf': [
          1,
          2,
          {
            'superKey': [
              3,
              4,
              {
                'megaKey': [5, 6]
              }
            ]
          }
        ]
      }
    ],
    'key2': {
      'key': 23,
      'fdasd': [
        {
          'nestedList': [
            7,
            8,
            {
              'deepKey': {
                'finalList': [
                  9,
                  10,
                  {
                    'ultraKey': [
                      11,
                      12,
                      {
                        'ultimateKey': [13, 14]
                      }
                    ]
                  }
                ]
              }
            }
          ]
        }
      ]
    }
  }
};

我需要创建一个和myMap完全独立的副本myMap2,但直接赋值myMap2 = myMap会产生引用,修改其中一个会影响另一个;用Map.of(myMap)这种浅拷贝也只能处理顶层结构,嵌套的List和Map还是共享引用。

目前我用手动递归的方式实现深拷贝:

Map deepCopy(Map original) {
  Map copy = {};
  original.forEach((key, value) {
    if (value is Map) {
      copy[key] = Map.of(deepCopy(value));
    } else if (value is List) {
      copy[key] = List.of(deepCopy2(value));
    } else {
      copy[key] = value;
    }
  });
  return copy;
}

List deepCopy2(List original) {
  List copy = [];
  for (var value in original) {
    if (value is Map) {
      copy.add(Map.of(deepCopy(value)));
    } else if (value is List) {
      copy.add(List.of(deepCopy2(value)));
    } else {
      copy.add(value);
    }
  }
  return copy;
}

Map myMap2 = deepCopy(myMap);

但考虑到数据的动态性,这个方案不够简洁。我也考虑过用jsonDecode(jsonEncode(data))实现深拷贝,但担心存在JSON不支持的类型,需要额外处理toEncodable参数。想请教有没有更简洁高效的方法,能确保所有嵌套结构完全独立?


解决方案

1. 优化手动递归代码

可以把两个递归函数合并成一个,用泛型处理所有类型,代码更简洁且覆盖所有场景:

dynamic deepCopy(dynamic value) {
  if (value is Map) {
    // 遍历Map的每个键值对,递归拷贝值
    return value.map((key, val) => MapEntry(key, deepCopy(val)));
  } else if (value is List) {
    // 遍历List的每个元素,递归拷贝
    return value.map((val) => deepCopy(val)).toList();
  } else {
    // 基础类型(int、String、bool等)都是不可变的,直接返回即可
    return value;
  }
}

// 使用方式
final myMap2 = deepCopy(myMap) as Map<String, dynamic>;

这个版本的优势:

  • 统一处理Map、List和基础类型,无需拆分两个函数
  • 基础类型直接返回,因为它们是不可变类型,修改不会影响原数据
  • 自动处理任意深度的嵌套结构

2. JSON序列化方案(适合纯JSON兼容类型)

如果服务器返回的所有数据都是JSON支持的类型(String、int、double、bool、List、Map),那么jsonDecode(jsonEncode(myMap))是最简洁的实现方式:

import 'dart:convert';

final myMap2 = jsonDecode(jsonEncode(myMap)) as Map<String, dynamic>;

注意事项:

  • 如果数据包含自定义类型,需要在jsonEncode时指定toEncodable来处理:
    final myMap2 = jsonDecode(jsonEncode(myMap, toEncodable: (obj) {
      if (obj is CustomModel) {
        return obj.toJson(); // 自定义类型需实现toJson方法
      }
      throw UnsupportedError('无法编码类型:${obj.runtimeType}');
    })) as Map<String, dynamic>;
    
  • JSON序列化会丢失非String类型的键(比如Map的键是int的话,会被转为String),如果有这种场景不适合用此方法。

3. 使用第三方包(快速实现)

如果项目允许引入第三方包,可以使用deep_collection包,它封装了完善的深拷贝逻辑:

首先在pubspec.yaml中添加依赖:

dependencies:
  deep_collection: ^1.0.0

然后使用:

import 'package:deep_collection/deep_collection.dart';

final myMap2 = myMap.deepCopy();

第三方包的优势是无需自己维护递归逻辑,已经处理了各种边缘场景,适合快速开发。


总结

  • 纯JSON兼容数据:优先用jsonDecode(jsonEncode(...)),代码最简洁
  • 需要保留类型或有自定义类型:使用优化后的手动递归方案,可靠且可控
  • 允许第三方包:用deep_collection这类成熟包,省心高效

内容的提问来源于stack exchange,提问作者Mohammed Hamdan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 19:30:59