#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;
}