DEV Community

Ge Ji
Ge Ji

Posted on

Dart Lesson 4: Collection Types (Part 2) — Maps and Enumerations

Today, we'll explore two more important data structures — Map and Enum — and dive into the characteristics and applications of immutable collections.

I. Map: A Collection of Key-Value Pairs

A Map is a collection of key-value pairs, similar to dictionaries or hash tables in other languages. It allows quick access values quickly through unique keys, making it ideal for storing associated data.

1. Creating a Map

Method 1: Using literals (Recommended)

void main() {
  // Create a Map with string keys and integer values
  Map<String, int> ages = {'Zhang San': 20, 'Li Si': 22, 'Wang Wu': 19};

  // Create a Map with integer keys and string values
  Map<int, String> weekdays = {1: 'Monday', 2: 'Tuesday', 7: 'Sunday'};

  print(ages); // Output: {Zhang San: 20, Li Si: 22, Wang Wu: 19}
  print(weekdays); // Output: {1: Monday, 2: Tuesday, 7: Sunday}
}
Enter fullscreen mode Exit fullscreen mode

Method 2: Using Map constructors

void main() {
  // Create empty Maps
  Map<String, String> emptyMap1 = {};
  Map<String, String> emptyMap2 = Map<String, String>();

  // Create a Map from entries
  Map<String, int> numberMap = Map.fromEntries([
    MapEntry('one', 1),
    MapEntry('two', 2),
  ]);
  print(numberMap); // Output: {one: 1, two: 2}
}
Enter fullscreen mode Exit fullscreen mode

2. Key-Value Pair Operations

Map operations revolve around keys, including adding, accessing, modifying, and deleting:

void main() {
  Map<String, String> capitals = {'China': 'Beijing', 'USA': 'Washington'};

  // Accessing values (through keys)
  print(capitals['China']); // Output: Beijing

  // Adding new key-value pairs
  capitals['Japan'] = 'Tokyo';
  print(capitals); // Output: {China: Beijing, USA: Washington, Japan: Tokyo}

  // Modifying values (through keys)
  capitals['USA'] = 'Washington, D.C.';
  print(
    capitals,
  ); // Output: {China: Beijing, USA: Washington, D.C., Japan: Tokyo}

  // Removing key-value pairs
  capitals.remove('Japan');
  print(capitals); // Output: {China: Beijing, USA: Washington, D.C.}

  // Checking if a key exists
  print(capitals.containsKey('China')); // Output: true

  // Checking if a value exists
  print(capitals.containsValue('London')); // Output: false

  // Getting all keys/values
  print(capitals.keys); // Output: (China, USA)
  print(capitals.values); // Output: (Beijing, Washington, D.C.)

  // Getting length
  print(capitals.length); // Output: 2

  // Clearing the Map
  capitals.clear();
  print(capitals); // Output: {}
}
Enter fullscreen mode Exit fullscreen mode

3. Iterating Over Maps

There are several ways to traverse a Map, with the forEach method being most most commonly used:

void main() {
  Map<String, double> prices = {'Apple': 5.99, 'Banana': 3.99, 'Orange': 4.50};

  // Method 1: Iterating with forEach (Recommended)
  prices.forEach((key, value) {
    print('The price of $key is \$$value');
  });
  // Output:
  // The price of Apple is $5.99
  // The price of Banana is $3.99
  // The price of Orange is $4.50

  // Method 2: Iterating over keys and accessing values
  for (String fruit in prices.keys) {
    print('$fruit: \${prices[fruit]}');
  }

  // Method 3: Iterating over entries (MapEntry)
  for (MapEntry<String, double> entry in prices.entries) {
    print('Key: ${entry.key}, Value: \${entry.value}');
  }
}
Enter fullscreen mode Exit fullscreen mode

II. Enums: Named Constants for Fixed Collections

Enums are special types used to define fixed sets of named constants. They're perfect for representing scenarios with clearly defined options (like genders, states, types, etc.).

1. Defining Enums

Use the enum keyword to define enums:

// Define an enum for gender
enum Gender {
  male, // Male
  female, // Female
  other, // Other
}

// Define an enum for order status
enum OrderStatus {
  pending, // Pending payment
  paid, // Paid
  shipped, // Shipped
  delivered, // Delivered
  cancelled, // Cancelled
}
Enter fullscreen mode Exit fullscreen mode

2. Using Enums

Enum values can be accessed directly and provide type safety:

enum Gender { male, female, other }

void main() {
  // Declare an enum variable
  Gender userGender = Gender.male;

  // Comparing enum values
  if (userGender == Gender.male) {
    print('User is male');
  }

  // Using enums with switch-case (Recommended, as compiler checks for completeness)
  switch (userGender) {
    case Gender.male:
      print('Gender: Male');
      break;
    case Gender.female:
      print('Gender: Female');
      break;
    case Gender.other:
      print('Gender: Other');
      break;
  }

  // Getting all enum values
  print(Gender.values); // Output: [Gender.male, Gender.female, Gender.other]

  // Getting the name of an enum value (as string)
  print(userGender.name); // Output: male

  // Getting an enum value by name
  Gender? gender = Gender.values.firstWhere(
    (g) => g.name == 'female',
    orElse: () => Gender.other,
  );
  print(gender); // Output: Gender.female
}
Enter fullscreen mode Exit fullscreen mode

3. Use Cases for Enums

Enums are particularly suitable for:

  • Representing fixed sets of options (genders, colors, seasons)
  • State machines (order statuses, network states)
  • Replacing "magic numbers" (improving code readability)

Anti-pattern (Not recommended):

// Using numbers for statuses (magic numbers, poor readability)
const int orderPending = 0;
const int orderPaid = 1;
Enter fullscreen mode Exit fullscreen mode

Best practice (Recommended):

// Using enums for statuses (clear and understandable)
enum OrderStatus { pending, paid, shipped }
Enter fullscreen mode Exit fullscreen mode

III. Immutable Collections (const/final) and Performance Impact

Collections are mutable by default (you can add/remove/modify elements), but in many scenarios we need immutable collections (cannot be modified after creation), which can be created using const or final.

1. Collections with final

A final collection has an immutable reference (cannot be reassigned), but its contents can be modified:

void main() {
  final List<int> numbers = [1, 2, 3];

  // Can modify collection contents
  numbers.add(4);
  print(numbers); // Output: [1, 2, 3, 4]

  // Cannot reassign (compilation error)
  // numbers = [5, 6, 7];
}
Enter fullscreen mode Exit fullscreen mode

2. Collections with const

A const collection is completely immutable (neither contents nor reference can be modified) and is created at compile time:

void main() {
  // Immutable List
  const List<int> immutableList = [1, 2, 3];

  // Immutable Set
  const Set<String> immutableSet = {'a', 'b'};

  // Immutable Map
  const Map<String, int> immutableMap = {'one': 1, 'two': 2};

  // Attempting to modify will cause errors
  // immutableList.add(4);  // Compilation error
  // immutableSet.remove('a');  // Compilation error
  // immutableMap['three'] = 3;  // Compilation error
}
Enter fullscreen mode Exit fullscreen mode

3. Performance Impact of Immutable Collections

Memory optimization: Identical const collections share memory (singleton), reducing memory usage.

void main() {
  const list1 = [1, 2, 3];
  const list2 = [1, 2, 3];
  print(identical(list1, list2)); // Output: true (same memory address)
}
Enter fullscreen mode Exit fullscreen mode
  • Performance improvement: const collections are initialized at compile time, no need to recreate them at runtime, making them suitable for frequently used fixed data.
  • Thread safety: Immutable collections are inherently thread-safe, no need to worry about concurrent modification in multi-threaded environments.
  • Use cases: Configuration data, constant lists, fixed options, and other data that doesn't need modification.

IV. Collection Type Comparison and Selection Guide

Collection Type Core Characteristics Typical Use Cases
List Ordered, allows duplicates, index access Sequential data (list displays, array calculations)
Set Unordered, no duplicates, fast lookup Deduplication, set operations (intersection/union)
Map Key-value pairs, fast access by key Associated data (configuration tables, dictionaries, caches)
Enum Fixed constant collection, type-safe Status representation, option selection, replacing magic numbers

Selection recommendations:

  • Use List when you need to store data in order
  • Use Set when you need to ensure data uniqueness
  • Use Map when you need to look up values by key
  • Use Enum when you need to represent fixed options or states

Top comments (0)