Dart - Copy/Clone Set (Shallow & Deep) Examples

This tutorial shows you how to copy or clone a Set in Dart, with explanation about the difference between shallow copy and deep copy.

One of the supported data types in Dart is Set. If you have a Set and you want to copy it into another Set, it can be done easily. However, you need to understand the difference between a shallow clone and a deep clone to avoid unexpected results.

Shallow Copy/Clone

With shallow copy, the main object which is the Set is copied, but the inner objects (the elements) are not. Whether shallow copy is enough or not depends on the element type. If the element type is immutable (such as String, bool, int, or bool) and you modify an element of the source Set, Dart will create a new object at a different memory location because it's immutable. Therefore, it doesn't affect the copied Set. It also applies the other way around. Shallow copy is quite simple as you can use the built-in methods provided by Dart.

Using Set.of

The first method you can use to copy a Set is the Set.of factory method. You have to pass an Iterable as the first argument which is the Set to be copied.

  factory Set.of(Iterable<E> elements)

Example:

  var originalTypes = {'grass', 'water', 'fire'};
  var newTypes = Set.of(originalSet);

Using Set.from

This method is similar to Set.of, but you can use it to produce a new Set whose element type can be a subtype of the source element type.

  factory Set.from(Iterable elements)

Example:

  final originalValues = <num>{10, 20, 30, 10};
  final newValues = Set.from(originalSet); // return Set<dynamic>
  final newValues = Set<int>.from(originalSet); // return Set<int>

Using Set.unmodifiable

If the copied Set cannot be modified, you can use Set.umodifiable factory method.

  factory Set.unmodifiable(Iterable<E> elements)

Example:

  var types = Set.unmodifiable({'grass', 'water', 'fire'});

Using Set.toSet

If you have a Set instance, you can also copy it into a new one using toSet instance method.

  Set toSet()

Example:

  var numbers = [10, 20, 30, 40];
  var numbersCopy = numbers.toSet();

Using Spread Operator

Dart's spread operator can also be used to copy a Set.

  var originalNumbers = <int>{10, 20, 30, 40};
  var newNumbers = {
    ...originalNumbers,
  };
  print(newNumbers); // {10, 20, 30, 40}

Deep Copy/Clone

As I have explained above, you can use shallow copy if the element type is immutable. However, if you have a Set with mutable element types, you need to be careful because if an element of the source Set is modified, the same element of the new Set will be affected. That's because Dart only copies the reference to the object, while the object itself is still at the same memory location. If you want each copied element to be a completely different object, you have to handle that by creating a new object for each copied element.

For example, we have a Set whose element type is Item. To make it easier, we create a factory method clone for creating a new Item instance from an existing one.

  class Item {
    int id;
    String name;
  
    Item({
      required this.id,
      required this.name,
    });
  
    factory Item.clone(Item source) {
      return Item(
        id: source.id,
        name: source.name,
      );
    }
  }

After that, use Iterable's map method to map each element and return a new Item by using the clone method.

  var item1 = Item(id: 1, name: 'One');
  var item2 = Item(id: 2, name: 'Two');
  Set<Item> source = {item1, item2};
  Set<Item> clone = source.map((o) => Item.clone(o)).toSet();
  item1.name = 'Three';
  print(source.where((item) => item.id == 1).first.name); // Three
  print(clone.where((item) => item.id == 1).first.name); // One

Summary

That's how to copy a Set in Dart. Shallow copy should be enough if the Set only contains immutable elements. If you have a Set with mutable elements and you want each element of the source and copied Sets to be two different objects, you have to perform deep copy by creating a new object for each element.