STL

Сортировка QList с использованием std::sort: строки, QCollator, структуры

обновлено 255 просмотров

Введение

QList — основной контейнер Qt, а в Qt 6 ещё и тот же класс, что QVector. Сортировка QList выполняется стандартным алгоритмом std::sort, а устаревший qSort в Qt 6 удалён. В этой статье собраны практические примеры сортировки QList с использованием std::sort: числа и строки, обратный порядок, строки без учёта регистра и по правилам русского алфавита, «естественная» сортировка имён файлов, пользовательские структуры и сортировка по нескольким полям.

Сортировка QList: числа и строки

std::sort принимает итераторы QList так же, как итераторы std::vector:

#include <QList>
#include <QString>
#include <algorithm>

QList<int> numbers = {5, 3, 8, 1};
std::sort(numbers.begin(), numbers.end());        // 1 3 5 8

QList<QString> names = {"Charlie", "Alice", "Bob"};
std::sort(names.begin(), names.end());            // Alice Bob Charlie

Для строк QString::operator< сравнивает коды символов Unicode. Это быстро, но не учитывает регистр и правила языка: строка «alice» окажется после «Zed», потому что строчные латинские буквы имеют большие коды, чем заглавные.

Сортировка по убыванию

#include <functional>

std::sort(names.begin(), names.end(), std::greater<QString>());

// или лямбдой
std::sort(numbers.begin(), numbers.end(), [](int a, int b) { return a > b; });

Для std::greater нужен заголовок <functional>.

Строки без учёта регистра: QStringList::sort

Для списка строк удобнее метод QStringList::sort(), который умеет игнорировать регистр:

QStringList list = {"banana", "Apple", "cherry", "apple"};
list.sort(Qt::CaseInsensitive);   // Apple apple banana cherry

То же самое через std::sort выглядит так:

std::sort(list.begin(), list.end(), [](const QString &a, const QString &b) {
    return QString::compare(a, b, Qt::CaseInsensitive) < 0;
});

Русский алфавит и «естественная» сортировка: QCollator

Чтобы строки с кириллицей, буквой «ё» и смешанным регистром сортировались так, как ожидает пользователь, используйте QCollator. Он сравнивает строки по правилам выбранной локали:

#include <QCollator>

QStringList words = {"ёж", "Яблоко", "арбуз", "Ель", "банан"};

QCollator collator(QLocale(QLocale::Russian));
collator.setCaseSensitivity(Qt::CaseInsensitive);

std::sort(words.begin(), words.end(), collator);
// арбуз банан Ель ёж Яблоко

Объект QCollator можно передать прямо в std::sort как компаратор. Включите числовой режим, и имена с номерами будут сортироваться «по-человечески»:

QStringList files = {"file10.txt", "file2.txt", "file1.txt"};

QCollator collator;
collator.setNumericMode(true);
std::sort(files.begin(), files.end(), collator);
// file1.txt file2.txt file10.txt  (а не file1, file10, file2)

Сортировка пользовательских структур

struct Person {
    QString name;
    int age;
};

QList<Person> people = {{"Иван", 30}, {"Анна", 25}, {"Пётр", 35}};

// по возрасту
std::sort(people.begin(), people.end(), [](const Person &a, const Person &b) {
    return a.age < b.age;
});

// по имени с учётом русского алфавита
QCollator collator(QLocale(QLocale::Russian));
std::sort(people.begin(), people.end(), [&](const Person &a, const Person &b) {
    return collator.compare(a.name, b.name) < 0;
});

Сортировка по нескольким полям

Если нужно отсортировать сначала по одному полю, а при равенстве — по другому, сравнивайте поля по очереди или используйте std::tie:

#include <tuple>

std::sort(people.begin(), people.end(), [](const Person &a, const Person &b) {
    return std::tie(a.age, a.name) < std::tie(b.age, b.name);
});

Другой способ — сначала отсортировать по второстепенному полю, а затем по основному с помощью std::stable_sort. Стабильная сортировка сохранит порядок записей с одинаковым значением основного поля.

Советы

  • Подключайте <algorithm> явно, не рассчитывайте на косвенное включение.
  • Для строк, которые видит пользователь, используйте QCollator, а не operator<.
  • Если QList разделяет данные с другой копией (implicit sharing), вызов begin() выполнит отсоединение. Это нормально, но не держите старые итераторы.
  • Сортировка списка указателей QList<Person*> требует компаратора, разыменовывающего указатели. Иначе будут сравниваться адреса.

Заключение

std::sort покрывает все типичные задачи: числа, строки, структуры. Для текстов на русском языке и имён с номерами подключайте QCollator, для сортировки без учёта регистра достаточно QStringList::sort(Qt::CaseInsensitive). Основы алгоритма описаны в статье Использование std::sort в C++, а сортировка ассоциативных контейнеров — в статье Как отсортировать QMap и QSet.