Dive Deep into Dart Lists: Best Practices Revealed


Dart

In the realm of programming, handling multiple data items often requires a structured approach. That's where collections come into play. In Dart, one of the foundational collections you'll encounter is the List. Let's delve into what Dart Lists are and how they differentiate from other collections.

 

Basic Definition and Concept of Dart Lists

In Dart, a List is an ordered collection of items. It allows multiple entries, including duplicates, and maintains the sequence of insertion. Dart lists are equivalent to arrays in many other programming languages but come with a richer set of methods to manipulate the elements.

A simple instantiation of a list looks like this:

var fruits = ['apple', 'banana', 'cherry'];

Here, fruits is a list containing three strings. You can also explicitly define the type of elements the list will hold:

List<String> vegetables = ['carrot', 'broccoli', 'spinach'];

With this definition, our vegetables list can only hold strings, ensuring type safety.

 

Differences Between List and Other Collections

Dart offers several types of collections, with List being just one of them. Understanding the differences between these can help you make the right choice for your specific use case:

List vs. Set: While both are collections, the main difference lies in how they handle duplicates. A List can have repeated elements, but a Set always contains unique items. For example:

var numberList = [1, 2, 2, 3]; // Valid in List
var numberSet = {1, 2, 2, 3}; // The repeated '2' would be ignored in a Set

List vs. Queue: A Queue is a collection that can be manipulated at both ends efficiently. You can add or remove items from the start or end of a queue in constant time. List, on the other hand, is optimized for indexed access, meaning accessing a specific position is its strength.

List vs. Map: A List is an ordered collection of items, while a Map is an unordered collection of key-value pairs. With lists, you access elements by their indices, but with maps, you access values using unique keys.

var studentList = ['Alice', 'Bob', 'Charlie'];
var studentMap = {
  'id1': 'Alice',
  'id2': 'Bob',
  'id3': 'Charlie'
};

 

Declaring and Initializing Lists

Dart offers developers multiple ways to declare and initialize lists. This flexibility ensures that whether you're looking for a more general approach or one tailored to specific types, there's likely a method suited for you. Let's delve into some of the common ways to work with "dart lists".

1. Using the List Constructor

The List constructor is a versatile way to create a list. You can create an empty list or specify its length. This is particularly useful when you know the size of the list in advance but don't have the values yet.

var emptyList = List<String>.empty(growable: true); // Creates an empty list which can grow in size.
var fixedLengthList = List<int>.filled(3, 0); // Creates a list of length 3, filled with zeros.

2. List Literals

One of the most straightforward methods to declare and initialize a list is using list literals. This is done by enclosing the comma-separated values in square brackets [].

var fruits = ['apple', 'banana', 'cherry']; // List of strings

This approach automatically infers the type based on the values provided. If you input integers, Dart knows it's a list of integers.

3. Type-specific Lists

Dart is a strong, statically-typed language. This means you can specify the type of data that your list will hold. It ensures type safety, which can prevent potential runtime errors. To declare a type-specific list, you use the syntax List<type>.

List<int> numbers = [1, 2, 3, 4, 5]; // This list will only accept integers.
List<String> names = ['Alice', 'Bob', 'Charlie']; // This list will only accept strings.

By explicitly mentioning the type, you set constraints on the kind of values the list can store. Any attempt to add a value of a different type will result in a compile-time error, ensuring the integrity of your data structures.

 

List Properties in Dart

When working with "dart lists", it's often necessary to access various properties of the list to gain insights or manipulate its content. Dart provides a set of handy properties that can be effortlessly utilized to achieve these tasks. Let's delve into some of these fundamental properties.

1. Length

The length property returns the number of elements in the list. This is especially useful when you want to iterate through the list or check if it has any elements.

var fruits = ['apple', 'banana', 'cherry'];
print(fruits.length);  // Outputs: 3

2. IsEmpty and isNotEmpty

These are boolean properties that help to quickly check the state of the list.

  • isEmpty: Returns true if the list has no elements.
  • isNotEmpty: Returns true if the list has one or more elements.

Both can be quite useful for conditional checks.

var emptyList = [];
print(emptyList.isEmpty);      // Outputs: true
print(emptyList.isNotEmpty);   // Outputs: false

3. First and Last Elements

When you want to quickly access the beginning or end of "dart lists", Dart provides two convenient properties:

  • first: Retrieves the first element in the list.
  • last: Retrieves the last element in the list.

However, be cautious; if the list is empty, accessing these properties will throw a runtime exception.

var numbers = [1, 2, 3, 4, 5];
print(numbers.first);  // Outputs: 1
print(numbers.last);   // Outputs: 5

 

Manipulating Lists in Dart

Manipulating "dart lists" is a common task in any Dart-based application, be it Flutter or web or server-side development. Dart provides a comprehensive suite of methods to help developers modify lists effectively. Here, we'll discuss the major methods for adding, removing, and updating elements within lists.

1. Adding Elements

add: This method appends a single element to the end of the list.

var fruits = ['apple', 'banana'];
fruits.add('cherry');
print(fruits);  // Outputs: ['apple', 'banana', 'cherry']

addAll: If you need to append multiple items at once, the addAll method comes in handy. It takes another list or iterable and appends its elements to the end of the list.

var vegetables = ['carrot', 'spinach'];
vegetables.addAll(['broccoli', 'cauliflower']);
print(vegetables);  // Outputs: ['carrot', 'spinach', 'broccoli', 'cauliflower']

2. Removing Elements

remove: Removes the first occurrence of the specified element from the list.

var numbers = [1, 2, 3, 2, 4];
numbers.remove(2);
print(numbers);  // Outputs: [1, 3, 2, 4]

removeAt: Removes the element at the specified index.

var colors = ['red', 'blue', 'green'];
colors.removeAt(1);
print(colors);  // Outputs: ['red', 'green']

removeLast: As the name suggests, it removes the last element of the list.

var animals = ['cat', 'dog', 'fish'];
animals.removeLast();
print(animals);  // Outputs: ['cat', 'dog']

removeWhere: This is a more advanced method that allows you to remove elements based on a condition. It takes a callback function and removes every item for which the callback returns true.

var integers = [1, 2, 3, 4, 5];
integers.removeWhere((num) => num % 2 == 0);  // Removes even numbers
print(integers);  // Outputs: [1, 3, 5]

3. Updating Elements

To update elements in "dart lists", you can use direct index-based assignment.

var weekdays = ['Mon', 'Tue', 'Weds'];
weekdays[2] = 'Wed';
print(weekdays);  // Outputs: ['Mon', 'Tue', 'Wed']

Additionally, you can use methods like map to update elements based on certain conditions. For instance, appending a suffix to each element:

var modifiedDays = weekdays.map((day) => '$day-day').toList();
print(modifiedDays);  // Outputs: ['Mon-day', 'Tue-day', 'Wed-day']

 

Iterating Over Lists in Dart

When working with "dart lists", it's often necessary to go through each element, whether you're processing the data or extracting specific items. Dart provides an array of methods to iterate over lists efficiently. In this section, we'll explore various methods to traverse and process the elements in lists.

1. Using the for loop

The traditional for loop allows you to iterate over each item in the list using an index.

var colors = ['red', 'green', 'blue'];
for (var i = 0; i < colors.length; i++) {
    print(colors[i]);
}

2. Using forEach method

The forEach method provides a more functional way of iterating over "dart lists". It takes a callback function which is applied to each item in the list.

colors.forEach((color) {
    print(color);
});

 

Map, Where, and Other Useful Methods

map: This method is used to transform each element in a list to a new form. It returns an iterable, so you might often chain it with toList() to get a new list.

var uppercaseColors = colors.map((color) => color.toUpperCase()).toList();
print(uppercaseColors);  // Outputs: ['RED', 'GREEN', 'BLUE']

where: Allows you to filter the elements of the list based on a condition. Just like map, it returns an iterable.

var longColors = colors.where((color) => color.length > 4).toList();
print(longColors);  // Outputs: ['green']

any and every: These methods are used to test conditions across elements of the list. any checks if at least one element satisfies a condition, while every checks if all elements meet the condition.

bool anyShortColor = colors.any((color) => color.length <= 3);
print(anyShortColor);  // Outputs: true

bool allLongColors = colors.every((color) => color.length > 4);
print(allLongColors);  // Outputs: false

reduce and fold: Both methods help in condensing the elements of the list into a single value. While they have their specifics, a simple example with reduce would be summing up a list of numbers.

var numbers = [1, 2, 3, 4];
var sum = numbers.reduce((a, b) => a + b);
print(sum);  // Outputs: 10

 

List Functions and Methods in Dart

"Dart lists" are equipped with a wide range of built-in functions and methods that facilitate different operations on lists. Let's delve into some commonly used methods that every Dart developer should be familiar with.

1. sort() method

The sort() method is used to arrange the elements in a list in ascending order (by default). For complex types or custom sorting criteria, you can provide a comparator function.

var numbers = [3, 1, 4, 2];
numbers.sort();
print(numbers);  // Outputs: [1, 2, 3, 4]

// Sorting with a comparator
var words = ['apple', 'banana', 'kiwi'];
words.sort((a, b) => b.length.compareTo(a.length));
print(words);  // Outputs: ['banana', 'apple', 'kiwi']

2. indexOf() and lastIndexOf()

These methods are used to find the position of an element in "dart lists". While indexOf() returns the first occurrence of the element, lastIndexOf() returns the last occurrence.

var fruits = ['apple', 'orange', 'apple', 'grape'];
print(fruits.indexOf('apple'));       // Outputs: 0
print(fruits.lastIndexOf('apple'));  // Outputs: 2

3. shuffle()

The shuffle() method randomly rearranges the elements of the list.

var colors = ['red', 'blue', 'green'];
colors.shuffle();
print(colors);  // Outputs could be: ['green', 'red', 'blue']

4. reversed

The reversed property returns an iterable that iterates over the list elements in reverse order. To convert this iterable back to a list, you can use the toList() method.

var alphabets = ['a', 'b', 'c'];
var reversedAlphabets = alphabets.reversed.toList();
print(reversedAlphabets);  // Outputs: ['c', 'b', 'a']

5. More Methods

"Dart lists" also offer a plethora of other methods for diverse tasks:

  • clear(): Empties the list.
  • fillRange(): Overwrites a range of elements with a specific value.
  • getRange(): Retrieves a range of elements from the list.
  • insert() and insertAll(): Adds elements at a specific index.
  • removeRange(): Removes a range of elements from the list.
  • setRange(): Overwrites a range of elements with elements from another list.
var animals = ['cat', 'dog', 'fish', 'bird'];
animals.removeRange(1, 3);
print(animals);  // Outputs: ['cat', 'bird']

 

Introduction to Nested Lists

In many programming scenarios, particularly when dealing with tabular, matrix, or grid data, a list of lists (or multi-dimensional lists) is invaluable. Just as a matrix has rows and columns, a two-dimensional list has two levels: the primary list level (often thought of as rows) and the nested list level (akin to columns).

1. How to Declare and Initialize

Declaration: To declare a multi-dimensional list, you essentially specify the type of its inner list.

List<List<int>> matrix;

Initialization: You can initialize it using list literals.

var matrix = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
];

2. Manipulating Multi-dimensional Lists

Accessing elements: You can access a specific element by specifying its row and column indices.

print(matrix[1][2]);  // Outputs: 6, which is the element in the second row and third column

Modifying elements: Similarly, you can modify an element using its indices.

matrix[1][2] = 10;
print(matrix[1]);  // Outputs: [4, 5, 10]

Iterating through elements: Nested loops are usually employed to traverse through each element of the nested "dart lists".

for (var row in matrix) {
  for (var element in row) {
    print(element);
  }
}

Adding new rows or columns: Rows can be added using the add method. For columns, you would typically loop through each row to add a new element.

matrix.add([10, 11, 12]);  // Adds a new row

for (var i = 0; i < matrix.length; i++) {
  matrix[i].add(i * 10);  // Adds a new column with values 0, 10, 20, 30...
}

Removing rows or columns: Rows can be easily removed using methods like removeAt. For columns, you would iterate through each row to remove an element at a specific index.

matrix.removeAt(1);  // Removes the second row

for (var row in matrix) {
  row.removeAt(2);  // Removes the third column from each row
}

 

List Spread, If, and For in Dart

In Dart, working with "dart lists" becomes even more flexible and concise with the introduction of the spread operator, list comprehensions using for, and the conditional inclusion using if. Let's dive into each of these features, accompanied by relevant examples.

1. Spread Operator ... and ...?

The spread operator ... is used to insert multiple elements from one list into another.

var list1 = [1, 2, 3];
var list2 = [4, ...list1];
print(list2);  // Outputs: [4, 1, 2, 3]

If the list being spread might be null and you wish to avoid errors, you can use the null-aware spread operator ...?.

var list3;
var list4 = [5, ...?list3];
print(list4);  // Outputs: [5]

2. List Comprehensions Using for

You can generate "dart lists" on-the-fly using list comprehensions with the for loop. It is a concise way to create lists based on operations or transformations.

var list5 = [for (var i = 0; i < 5; i++) i * i];
print(list5);  // Outputs: [0, 1, 4, 9, 16]

3. Conditional Element Inclusion Using if

With "dart lists", you can conditionally include elements using the if clause, allowing you to filter or decide elements at the time of list creation.

var includeOdd = true;
var numbers = [1, 2, 3, 4, 5];
var filteredList = [for (var num in numbers) if (includeOdd && num.isOdd) num];
print(filteredList);  // Outputs: [1, 3, 5]

By chaining if and for together, you can achieve more complex behaviors:

var matrix = [
  [1, 2],
  [3, 4],
  [5, 6]
];
var flattenedList = [for (var row in matrix) for (var item in row) item];
print(flattenedList);  // Outputs: [1, 2, 3, 4, 5, 6]

 

Immutability with Dart Lists

When working with "dart lists" in Dart, there are occasions where you'd want the data within the list to remain unchanged, ensuring the integrity and predictability of your application. Dart provides mechanisms to achieve this with const lists and unmodifiable lists. Let's explore both.

1. const Lists

In Dart, a const list is a list that's compile-time constant. This means you can't modify it at runtime.

const myList = [1, 2, 3, 4, 5];

// This will cause a compile-time error:
// myList.add(6); 

2. Unmodifiable Lists

Unlike const lists, which are compile-time constants, unmodifiable lists are runtime constants. You can create an unmodifiable list by calling the List.unmodifiable constructor.

var originalList = [1, 2, 3];
var unmodifiableList = List.unmodifiable(originalList);

// This will throw a runtime error:
// unmodifiableList.add(4);

 

Differences between Dart List, Set, and Queue

Feature/Property List Set Queue
Definition An ordered collection of items. An unordered collection of unique items. A collection of items that can be accessed and modified at both ends.
Order Maintains order. Does not maintain a specific order. Maintains order.
Duplicates Allows duplicates. Does not allow duplicates. Allows duplicates.
Access Indexed access (e.g., list[0]). No indexed access. Front and rear access.
Methods add(), remove(), insert(), etc. add(), remove(), contains(), etc. addFirst(), removeLast(), etc.
Use Case When order and direct access are important. When you want to ensure elements are unique. When you need first-in-first-out (FIFO) or last-in-first-out (LIFO) access.

 

Conclusion

In Dart, the List stands out as one of the most widely used collections, primarily because of its flexibility and the natural ways we often think about sequences of data. With its ordered nature and ability to contain duplicates, the List is suitable for a wide range of tasks, from simple arrays of data to more complex data structures. While Set and Queue have their unique qualities and use cases, the "dart lists" remain central in Dart programming for their accessibility and familiarity to developers.

 

Further Reading

For more in-depth information and nuances about these collections:

 

Deepak Prasad

Deepak Prasad

He is the founder of GoLinuxCloud and brings over a decade of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels in various domains, from development to DevOps, Networking, and Security, ensuring robust and efficient solutions for diverse projects. You can connect with him on his LinkedIn profile.

Can't find what you're searching for? Let us assist you.

Enter your query below, and we'll provide instant results tailored to your needs.

If my articles on GoLinuxCloud has helped you, kindly consider buying me a coffee as a token of appreciation.

Buy GoLinuxCloud a Coffee

For any other feedbacks or questions you can send mail to admin@golinuxcloud.com

Thank You for your support!!

Leave a Comment