DEV Community

Harsh Mishra
Harsh Mishra

Posted on

List: Container, C++

List Container in C++: Usage and Operations

In C++, the list container is part of the Standard Template Library (STL) and provides a doubly linked list implementation. Unlike vectors, lists are optimized for efficient insertions and deletions at both ends and in the middle. However, they do not provide random access to elements. Lists are particularly useful when you need to frequently add or remove elements from the middle or the ends of the container, but you don't need constant-time random access.

In this guide, we will cover everything you need to know about the list container, including how to construct lists, how to perform common operations, and how to make the most of its capabilities in C++.

Table of Contents

  1. Introduction to List in C++
  2. Different Ways to Construct a List
  3. Common Operations on List
    • push_back()
    • push_front()
    • pop_back()
    • pop_front()
    • insert()
    • erase()
    • size()
    • empty()
    • clear()
  4. Working with Custom Data Types
  5. Iterators in Lists
  6. List of Lists
  7. Summary and Best Practices

1. Introduction to List in C++

A list in C++ is a sequence container that implements a doubly linked list. Unlike vectors, lists allow fast insertions and deletions at any point in the container. However, they do not provide constant-time random access to elements, making them less suitable for situations where you need quick access by index. Lists are often used when frequent modifications (insertions or deletions) in the middle of the sequence are needed.

Key Features:

  • Dynamic Sizing: Lists grow and shrink dynamically as elements are added or removed.
  • Efficient Insertions and Deletions: Lists are optimized for adding and removing elements anywhere within the sequence.
  • No Random Access: Lists do not allow direct access to elements by index; instead, you can iterate through them.
  • Doubly Linked List: Each element points to both its previous and next elements, enabling traversal in both directions.

2. Different Ways to Construct a List

You can construct a list in C++ in several ways, depending on the desired behavior.

2.1 Default Constructor

The default constructor creates an empty list.

list<int> l;
Enter fullscreen mode Exit fullscreen mode

This creates an empty list of integers, and you can add elements later.

2.2 Constructor with Initial Size

You can create a list with a specific size, where each element is initialized to the default value for the element type.

list<int> l(5);  // Creates a list of size 5 with default-initialized elements (0 for int)
Enter fullscreen mode Exit fullscreen mode

To initialize the elements to a specific value, provide a second argument.

list<int> l(5, 10);  // Creates a list of size 5 with each element initialized to 10
Enter fullscreen mode Exit fullscreen mode

2.3 Constructor with an Initializer List

You can initialize a list directly with a list of values.

list<int> l = {1, 2, 3, 4, 5};
Enter fullscreen mode Exit fullscreen mode

This creates a list containing the values 1, 2, 3, 4, and 5.

2.4 Constructor with Another Container

You can also create a list by copying the contents of another container (like another list or array).

list<int> l1 = {1, 2, 3, 4, 5};
list<int> l2(l1);  // Creates a list that is a copy of l1
Enter fullscreen mode Exit fullscreen mode

2.5 Constructor with Iterators

You can initialize a list using a range of iterators from another container. This allows you to construct a list from other types of containers such as vectors, deques, or arrays.

vector<int> v = {1, 2, 3, 4, 5};
list<int> l(v.begin(), v.end());  // Initializes a list using iterators from a vector
Enter fullscreen mode Exit fullscreen mode

You can also initialize a list from arrays, using the array's beginning and ending pointers.

int arr[] = {1, 2, 3, 4, 5};
list<int> l(arr, arr + 5);  // Initializes a list using pointers to an array
Enter fullscreen mode Exit fullscreen mode

2.6 Assignment Operator

You can use the assignment operator to copy elements from one list to another.

list<int> l1 = {1, 2, 3};
list<int> l2;
l2 = l1;  // Now l2 contains the same elements as l1
Enter fullscreen mode Exit fullscreen mode

The assignment operator performs a deep copy, meaning that the elements of l1 are copied into l2, not just the pointer or reference.


3. Common Operations on List

3.1 push_back()

The push_back() function adds an element to the end of the list. This operation is constant time.

Syntax:

l.push_back(element);
Enter fullscreen mode Exit fullscreen mode

Example:

list<int> l;
l.push_back(10);
l.push_back(20);
l.push_back(30);
Enter fullscreen mode Exit fullscreen mode

After this, the list l will contain: [10, 20, 30].

3.2 push_front()

The push_front() function adds an element to the front of the list. This operation is also constant time.

Syntax:

l.push_front(element);
Enter fullscreen mode Exit fullscreen mode

Example:

list<int> l = {10, 20, 30};
l.push_front(5);  // Adds 5 at the front
Enter fullscreen mode Exit fullscreen mode

After this, the list l will contain: [5, 10, 20, 30].

3.3 pop_back()

The pop_back() function removes the last element from the list. This operation is constant time.

Syntax:

l.pop_back();
Enter fullscreen mode Exit fullscreen mode

Example:

list<int> l = {10, 20, 30};
l.pop_back();  // Removes 30
Enter fullscreen mode Exit fullscreen mode

After calling pop_back(), the list will contain: [10, 20].

3.4 pop_front()

The pop_front() function removes the first element from the list.

Syntax:

l.pop_front();
Enter fullscreen mode Exit fullscreen mode

Example:

list<int> l = {10, 20, 30};
l.pop_front();  // Removes 10
Enter fullscreen mode Exit fullscreen mode

After calling pop_front(), the list will contain: [20, 30].

3.5 insert()

The insert() function allows you to insert an element at a specific position in the list. It can insert a single element, multiple identical elements, or elements from another container.

Syntax:

  • Insert a single element:
  l.insert(position, element);
Enter fullscreen mode Exit fullscreen mode
  • Insert multiple identical elements:
  l.insert(position, count, element);
Enter fullscreen mode Exit fullscreen mode
  • Insert elements from another container:
  l.insert(position, container.begin(), container.end());
Enter fullscreen mode Exit fullscreen mode

Example 1: Insert a Single Element

list<int> l = {10, 20, 30};
auto it = l.begin();
advance(it, 1);
l.insert(it, 15);  // Inserts 15 at position 1
Enter fullscreen mode Exit fullscreen mode

After this, the list l will contain: [10, 15, 20, 30].

Example 2: Insert Multiple Identical Elements

l.insert(it, 3, 100);  // Inserts three 100's at position 1
Enter fullscreen mode Exit fullscreen mode

After this, the list l will contain: [10, 100, 100, 100, 15, 20, 30].

Example 3: Insert Elements from Another Container

list<int> l1 = {1, 2, 3};
list<int> l2 = {4, 5, 6};
l1.insert(l1.end(), l2.begin(), l2.end());  // Inserts all elements from l2 at the end of l1
Enter fullscreen mode Exit fullscreen mode

After this, the list l1 will contain: [1, 2, 3, 4, 5, 6].

3.6 erase()

The erase() function removes elements from the list at a specified position or a range of positions.

Syntax:

  • Erase a single element:
  l.erase(position);
Enter fullscreen mode Exit fullscreen mode
  • Erase a range of elements:
  l.erase(start, end);  // Removes elements from start to end-1
Enter fullscreen mode Exit fullscreen mode

Example 1: Erase a Single Element

list<int> l = {10, 20, 30};
auto it = l.begin();
advance(it, 1);
l.erase(it);  // Removes element at position 1 (20)
Enter fullscreen mode Exit fullscreen mode

After this, the list l will contain: [10, 30].

Example 2: Erase a Range of Elements

list<int> l = {10, 20, 30, 40, 50};
auto it1 = l.begin();
advance(it1, 1);
auto it2 = l.begin();
advance(it2, 4);
l.erase(it1, it2);  // Removes elements from position 1 to 3 (20, 30, 40)
Enter fullscreen mode Exit fullscreen mode

After this, the list l will contain: [10, 50].

3.7 size()

The size() function returns the number of elements in the list.

Syntax:

size_t size = l.size();
Enter fullscreen mode Exit fullscreen mode

Example:

list<int> l = {10, 20, 30};
cout << "Size of list: " << l.size() << endl;  // Outputs: 3
Enter fullscreen mode Exit fullscreen mode

3.8 empty()

The empty() function checks whether the list is empty.

Syntax:

bool isEmpty = l.empty();
Enter fullscreen mode Exit fullscreen mode

Example:

list<int> l;
cout << "Is list empty? " << (l.empty() ? "Yes" : "No") << endl;  // Outputs: Yes
l.push_back(10);
cout << "Is list empty? " << (l.empty() ? "Yes" : "No") << endl;  // Outputs: No
Enter fullscreen mode Exit fullscreen mode

3.9 clear()

The clear() function removes all elements from the list.

Syntax:

l.clear();
Enter fullscreen mode Exit fullscreen mode

Example:

list<int> l = {10, 20, 30};
l.clear();  // Removes all elements
Enter fullscreen mode Exit fullscreen mode

After calling clear(), the list will be empty.


4. Working with Custom Data Types

Lists can also store custom data types such as classes or structs. When working with custom types, define appropriate constructors, destructors, and operators if needed.

Example:

class Student {
public:
    string name;
    int age;

    Student(string n, int a) : name(n), age(a) {}
};

list<Student> students;
students.push_back(Student("John", 20));
students.push_back(Student("Alice", 22));
Enter fullscreen mode Exit fullscreen mode

5. Iterators in Lists

Lists support iterators that allow you to iterate over the elements. The begin() and end() functions return iterators pointing to the first and one-past-the-last elements of the list, respectively.

Example Using Iterators

list<int> l = {10, 20, 30};
for (auto it = l.begin(); it != l.end(); ++it) {
    cout << *it << " ";  // Output: 10 20 30
}
Enter fullscreen mode Exit fullscreen mode

6. List of Lists

A list can hold other lists, allowing you to create complex data structures, like matrices or nested lists.

Example:

list<list<int>> matrix = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
Enter fullscreen mode Exit fullscreen mode

You can access elements with two iterators or loops.

for (auto& inner_list : matrix) {
    for (auto elem : inner_list) {
        cout << elem << " ";  // Outputs: 1 2 3 4 5 6 7 8 9
    }
}
Enter fullscreen mode Exit fullscreen mode

7. Summary and Best Practices

  • Lists are great for situations requiring frequent insertions or deletions, especially in the middle of the container.
  • If you need random access or a fixed-size container, vectors are a better choice.
  • Always use push_front() or push_back() for efficient additions and pop_front() or pop_back() for efficient removals.
  • Lists are less efficient when it comes to access time since they don’t allow direct indexing.
  • If you need to store custom types in a list, ensure that the types are properly designed with constructors and operators.

By understanding and leveraging lists in C++, you can effectively manage dynamic collections with optimized performance for adding and removing elements.

Top comments (0)