Containers in Qt5

In this part of the Qt5 tutorial, we will talk about containers in Qt5. The following containers are mentioned: QVector, QList, QStringList, QSet, and QMap.

Containers are general-purpose classes that store items of a given type in memory. C++ has the Standard Template Library (STL), which has its own containers. It Qt, we can use Qt containers or STL containers.

There are two kinds of containers: sequential and associative. Sequential containers store items one after another, whereas associative containers store key-value pairs. QList, QVector, QLinkedList belong to sequential containers; QMap and QHash are examples of associative containers.

QVector

QVector is a template class that provides a dynamic array. It stores its items in adjacent memory locations and provides fast index-based access. For large vectors, inserting operations are slower and QList container is recommended instead.

myvector.cpp
#include <QVector>
#include <QTextStream>

int main(void) {

    QTextStream out(stdout);

    QVector<int> vals = {1, 2, 3, 4, 5};
    
    out << "The size of the vector is: " << vals.size() << endl;
       
    out << "The first item is: " << vals.first() << endl;
    out << "The last item is: " << vals.last() << endl;
    
    vals.append(6);
    vals.prepend(0);   
    
    out << "Elements: "; 
    
    for (int val : vals) {
        out << val << " ";
    }    
    
    out << endl;
   
    return 0;
}

The example works with a vector of integers.

QVector<int> vals = {1, 2, 3, 4, 5};

A vector of integers is created.

out << "The size of the vector is: " << vals.size() << endl;

The size() method gives the size of the vector—the number of items in the vector.

out << "The first item is: " << vals.first() << endl;

The first item is retrieved with the first() method.

out << "The last item is: " << vals.last() << endl;

The last item of the vector is found with the last() method.

vals.append(6);

The append() method inserts the value at the end of the vector.

vals.prepend(0);

The prepend() method inserts the value at the beginning of the vector.

for (int val : vals) {
    out << val << " ";
}    

We go through the vector in the for loop and print its contents.

Output
$ ./myvector 
The size of the vector is: 5
The first item is: 1
The last item is: 5
Elements: 0 1 2 3 4 5 6 

QList

QList is a container for creating a list of elements. It is similar to QVector. It stores a list of values and provides fast index-based access as well as fast insertions and removals. It is one of the most commonly used containers in Qt.

mylist.cpp
#include <QTextStream>
#include <QList>
#include <algorithm>

int main(void) {

    QTextStream out(stdout);

    QList<QString> authors = {"Balzac", "Tolstoy", 
        "Gulbranssen", "London"};
  
    for (int i=0; i < authors.size(); ++i) {
  
        out << authors.at(i) << endl;
    }  
  
    authors << "Galsworthy" << "Sienkiewicz";
    
    out << "***********************" << endl;

    std::sort(authors.begin(), authors.end());

    out << "Sorted:" << endl;
    for (QString author : authors) {
  
        out << author << endl;
    }  
}

The example presents the QList container.

QList<QString> authors = {"Balzac", "Tolstoy", 
    "Gulbranssen", "London"};

A QList container is created. It stores the names of writers.

for (int i=0; i < authors.size(); ++i) {

    out << authors.at(i) << endl;
}  

In a for loop, we go through the container and print its elements. The at() method returns the item at the given index.

authors << "Galsworthy" << "Sienkiewicz";

The << operator is used to insert two new items into the list.

std::sort(authors.begin(), authors.end());

The std::sort() method sorts the list in ascending order.

out << "Sorted:" << endl;
for (QString author : authors) {

    out << author << endl;
} 

Now we print the sorted list.

Output
$ ./mylist 
Balzac
Tolstoy
Gulbranssen
London
***********************
Sorted:
Balzac
Galsworthy
Gulbranssen
London
Sienkiewicz
Tolstoy

QStringList

QStringList is a convenience container that provides a list of strings. It has fast index-based access as well as fast insertions and removals.

mystringlist.cpp
#include <QTextStream>
#include <QList>

int main(void) {

    QTextStream out(stdout);

    QString string = "coin, book, cup, pencil, clock, bookmark";
    QStringList items = string.split(",");
    QStringListIterator it(items);
    
    while (it.hasNext()) {
        out << it.next().trimmed() << endl;    
    }
}

In the example, we create a list of strings from a string and print the elements into the console.

QString string = "coin, book, cup, pencil, clock, bookmark";
QStringList items = string.split(",");

The QString's split() method cuts the string into substrings according to the provided separator. The substrings are returned in a list.

QStringListIterator it(items);

QStringListIterator provides a Java-style const iterator for QStringList.

while (it.hasNext()) {
    out << it.next().trimmed() << endl;    
}

With the created iterator, we print the elements of the list to the terminal. The trimmed() method trims the white space in the string element.

Output
$ ./mystringlist 
coin
book
cup
pencil
clock
bookmark

QSet

QSet provides a single-valued mathematical set with fast lookups. The values are stored in an unspecified order.

myset.cpp
#include <QSet>
#include <QList>
#include <QTextStream>
#include <algorithm>

int main(void) {
    
    QTextStream out(stdout);

    QSet<QString> cols1 = {"yellow", "red", "blue"};
    QSet<QString> cols2 = {"blue", "pink", "orange"};
      
    out << "There are " << cols1.size() << " values in the set" << endl;
   
    cols1.insert("brown");
   
    out << "There are " << cols1.size() << " values in the set" << endl;
    
    cols1.unite(cols2);
    
    out << "There are " << cols1.size() << " values in the set" << endl;
    
    for (QString val : cols1) {
        out << val << endl;
    }
    
    QList<QString> lcols = cols1.values();
    std::sort(lcols.begin(), lcols.end());
    
    out << "*********************" << endl;
    out << "Sorted:" << endl;
    
    for (QString val : lcols) {
        out << val << endl;
    }    
   
   return 0;
}

The QSet is used to store colours in the example. It makes no sense to have one colour value specified more times.

QSet<QString> cols1 = {"yellow", "red", "blue"};
QSet<QString> cols2 = {"blue", "pink", "orange"};

We have two sets of colour values. Blue colour is located in both sets.

out << "There are " << cols1.size() << " values in the set" << endl;

The size() method returns the size of the set.

cols1.insert("brown");

We add a new value to a set with the insert() method.

cols1.unite(cols2);

The unite(( method performs a union of two sets. The cols1 set will have all items inserted from cols2 set that are not already present; in our case, all except for the colour blue.

for (QString val : cols1) {
    out << val << endl;
}

With the for loop, we print all the items in the cols1 set.

QList<QString> lcols = cols1.values();
std::sort(lcols.begin(), lcols.end());

Sorting of a set is not supported. We can create a list out of a set and sort it. The values() method returns a new QList containing the elements in the set. The order of the elements in the QList is undefined.

Output
$ ./myset 
There are 3 values in the set
There are 4 values in the set
There are 6 values in the set
pink
orange
brown
blue
yellow
red
*********************
Sorted:
blue
brown
orange
pink
red
yellow

QMap

QMap is an associative array (dictionary) that stores key-value pairs. It provides fast lookup of the value associated with a key.

myqmap.cpp
#include <QTextStream>
#include <QMap>

int main(void) {

    QTextStream out(stdout);

    QMap<QString, int> items = { {"coins", 5}, {"books", 3} };
    
    items.insert("bottles", 7);
    
    QList<int> values = items.values();
    
    out << "Values:" << endl;

    for (int val : values) {
        out << val << endl;
    }
    
    QList<QString> keys = items.keys();

    out << "Keys:" << endl;
    for (QString key : keys) {
        out << key << endl;
    }    
    
    QMapIterator<QString, int> it(items);
    
    out << "Pairs:" << endl;
    
    while (it.hasNext()) {
        it.next();
        out << it.key() << ": " << it.value() << endl;    
    }
}

In the example, we have a dictionary where we map string keys to integer values.

QMap<QString, int> items = { {"coins", 5}, {"books", 3} };

A QMap is created. It has two pairs.

items.insert("bottles", 7);

A new pair is inserted with the insert() method.

QList<int> values = items.values();

out << "Values:" << endl;

for (int val : values) {
    out << val << endl;
}

We get all the values of the dictionary and print them to the console. The values() method returns a list of map values.

QList<QString> keys = items.keys();

out << "Keys:" << endl;
for (QString key : keys) {
    out << key << endl;
} 

Likewise, we print all the keys of the dictionary. The keys() method returns a list containing all the keys in the dictionary.

QMapIterator<QString, int> it(items);

QMapIterator is a Java-style iterator for a QMap. It can be used to iterate over elements of a map.

while (it.hasNext()) {
    it.next();
    out << it.key() << ": " << it.value() << endl;    
}

With the help of the iterator, we walk over all elements of the map. The key() method returns the current key and the value() method returns the current value.

Output
$ ./myqmap 
Values:
3
7
5
Keys:
books
bottles
coins
Pairs:
books: 3
bottles: 7
coins: 5

Custom class sorting

In the following example, we are going to sort objects of a custom class in a QList.

book.h
class Book {
    
    public:
        Book(QString, QString);
        QString getAuthor() const;
        QString getTitle() const;
        
    private:            
        QString author;
        QString title;    
};

This is the header file for our custom Book class.

book.cpp
#include <QString>
#include "book.h"

Book::Book(QString auth, QString tit) {
    
    author = auth;
    title = tit;
}

QString Book::getAuthor() const {
    
    return author;
}

QString Book::getTitle() const {
    
    return title;
}

This is the implementation of the Book class; we have two accessor methods.

sortcustomclass.cpp
#include <QTextStream>
#include <QList>
#include <algorithm> 
#include "book.h"

bool compareByTitle(const Book &b1, const Book &b2) {
    
  return b1.getTitle() < b2.getTitle();
}

int main(void) {

    QTextStream out(stdout);

    QList<Book> books = {
        Book("Jack London", "The Call of the Wild"),
        Book("Honoré de Balzac", "Father Goriot"),
        Book("Leo Tolstoy", "War and Peace"),
        Book("Gustave Flaubert", "Sentimental education"),
        Book("Guy de Maupassant", "Une vie"),
        Book("William Shakespeare", "Hamlet")
    };
    
    std::sort(books.begin(), books.end(), compareByTitle);
    
    for (Book book : books) {
        out << book.getAuthor() << ": " << book.getTitle() << endl;
    }
}

In the example, we create a few book objects and sort them with the std::sort algorithm.

bool compareByTitle(const Book &b1, const Book &b2) {
    
  return b1.getTitle() < b2.getTitle();
}

The compareByTitle() is a comparison function used by the sort algorithm.

std::sort(books.begin(), books.end(), compareByTitle);

The std::sort algorithm sorts the books in the list by the book's title.

Output
$ ./sortcustomclass 
Honoré de Balzac: Father Goriot
William Shakespeare: Hamlet
Gustave Flaubert: Sentimental education
Jack London: The Call of the Wild
Guy de Maupassant: Une vie
Leo Tolstoy: War and Peace

In this chapter, we have worked with Qt's containers.