Переопределение QAbstractItemModel::index и доступ к std::map

#c #qt

#c #qt

Вопрос:

В моей программе я хочу использовать шаблон view / model с view = QListView и мою собственную модель, которую я выделил из QAbstractListModel. Мой класс данных выглядит следующим образом

 class Avtomat
{
...
 map<QString, State *> states;
...
};
  

В моем классе моделей

 class AvtomatModel : public QAbstractListModel
{
    ...
    Avtomat a;
    ...
};
  

Я пытаюсь перегрузить функцию QAbstractItemView :: index, чтобы я мог предоставить интерфейс для редактирования карты данных.
Поскольку функция index принимает аргумент строки int, я решил эту проблему, предоставив следующее

 State* Avtomat::pStateFromIndex(int index) const
{
    map<QString, State *>::const_iterator i;
    int count = 0;
    for (i = states.begin(); i != states.end() amp;amp; count != index;   i)
          count;
    return (*i).second;
}
  

итак, в моей индексной функции мне нравится это

 return createIndex(row, column, a.pStateFromIndex(row));
  

но это кажется довольно уродливым, потому что у меня есть O (n). Можете ли вы помочь мне разработать лучший способ доступа к моей карте с использованием int index?

Ответ №1:

Это фундаментальная проблема моделирования данных. Какой основной способ вам нужен для извлечения ваших данных? По ключу или по индексу?

Если вы обращаетесь к нему только по индексу (в том числе в модели), то вы просто используете неподходящую структуру данных и должны переключиться на что-то другое, например, список.

Если вам тоже нужно запрашивать по ключу, у вас есть несколько вариантов. В том, что вы уже делаете, нет ничего плохого, если эффективность не является огромным фактором, особенно если набор данных невелик. В качестве альтернативы вы также могли бы поддерживать сопоставления ключа и индекса с вашими базовыми данными. Это простое и эффективное решение, но это означает, что вы должны взять на себя управление согласованностью между ними и имеет нагрузку на память, которая может быть значительной, если ваш набор данных большой. Или вы могли бы использовать структуру данных, которая обеспечивает доступ как по ключу, так и по индексу напрямую. В конечном счете, это зависит от ваших конкретных обстоятельств и домена данных, с которым вы работаете.

В документации есть хорошее краткое описание контейнерных классов Qt (наряду с контейнерами std). Раздел о алгоритмической сложности может быть вам особенно интересен.

Ответ №2:

Другой вариант — использовать вектор для хранения данных в парах ключ-значение. Затем к вектору можно получить доступ по индексу или по ключу. Недостатком этого является то, что вставка в вектор является дорогостоящей по сравнению с std::map.

 typedef std::pair<QString, State*> StateP;
typedef std::vector<StateP> States;
States states;
  

Затем сохраняйте вектор в отсортированном порядке на основе предиката, который сравнивает первый элемент. Вы можете выполнять поиск элементов по индексу в O (1) или по ключу в O (log n).

 struct StatePCompare {
    bool operator()(StateP constamp; lhs, StateP constamp; rhs) const {
        return (lhs.first < rhs.first);
    }
};

void Avtomat::insert(QString key, State* state) 
{
    States::iterator i = std::lower_bound(states.begin(), states.end(), StatePCompare());
    if ((i != states.end() amp;amp; (i->first == key)) {
        // key already exists, set the element
        i->second = state;
    }
    else {
        states.insert(i, state);
    }
}

State* Avtomat::find(QString key) 
{
    States::iterator i = std::lower_bound(states.begin(), states.end(), StatePCompare());
    if ((i != states.end() amp;amp; (i->first == key)) {
        return i->second;
    }
    return NULL;
}