Qt 之 连续缓存QContiguousCache

背景

曾经面试遇到过面试官问这个问题:如何展示大量问题,当时简单说了下心里的想法,后来发现跟QContiguousCache有些类似,现在记录下来


问题描述:

当应用程序尤其是Gui程序,需要展示大量数据,例如QListview等,尤其实在内存是有限时。

QContiguousCache类是一个模板类,它提供了一个连续的高速缓存。

所述QContiguousCache类提供了一种在用户界面视图缓存用于显示项目的有效方式。与QCache不同,它增加了一个限制,即缓存中的元素是连续的。这具有匹配用户界面查看最常见请求数据的方式的优点,作为定位在当前滚动位置周围的一组行。此限制允许缓存消耗比QCache更少的内存和处理器周期。

QContiguousCache对固定容量进行操作,使用setCapacity ()设置或作为参数传递给构造函数。这个容量是缓存本身的内存使用上限,不包括元素本身分配的内存。请注意,容量为零(默认值)的缓存意味着不会存储任何项目:insert ()、append () 和prepend () 操作实际上是无操作的。因此,在将项目添加到缓存之前将容量设置为合理的值非常重要。

使用连续缓存的最简单方法是使用append () 和prepend ()。

MyRecord record(int row) const
{
    Q_ASSERT(row >= 0 && row < count());

    while(row > cache.lastIndex())
        cache.append(slowFetchRecord(cache.lastIndex()+1));
    while(row < cache.firstIndex())
        cache.prepend(slowFetchRecord(cache.firstIndex()-1));

    return cache.at(row);
}

如果缓存已满,则将删除缓存另一端添加或添加新项目的项目。

在请求的行与当前缓存的项目相距很远的情况下,可以通过使用insert () 函数进一步优化此用法。如果插入新项目的位置与当前缓存的项目之间存在间隙,则首先删除现有的缓存项目以保留缓存的连续性。因此,重要的是在使用insert ()时要小心,以避免不必要的缓存清除。

QContiguousCache类的有效索引范围是从 0 到 INT_MAX。调用prepend () 使第一个索引变为小于 0 或调用append () 使最后一个索引变为大于 INT_MAX 会导致缓存的索引无效。当缓存索引无效时,重要的是在调用containsIndex ()、firstIndex ()、lastIndex ()、at () 或operator[] ()之前调用normalizeIndexes ()。当缓存具有无效索引时调用这些函数将导致未定义的行为。可以使用areIndexesValid检查索引()

在大多数情况下,索引不会超过 0 到 INT_MAX,并且不需要使用normalizeIndexes ()。


实例如下:

提示:这里填写问题的分析:
例如:Handler 发送消息有两种方式,分别是 Handler.obtainMessage()和 Handler.sendMessage(),其中 obtainMessage 方式当数据量过大时,由于 MessageQuene 大小也有限,所以当 message 处理不及时时,会造成先传的数据被覆盖,进而导致数据丢失。


#ifndef RANDOMLISTMODEL_H
#define RANDOMLISTMODEL_H

#include 
#include 

QT_FORWARD_DECLARE_CLASS(QTimer)

class RandomListModel : public QAbstractListModel
{
    Q_OBJECT
public:
    RandomListModel(QObject *parent = nullptr);
    ~RandomListModel();

    int rowCount(const QModelIndex & = QModelIndex()) const override;
    QVariant data(const QModelIndex &, int) const override;

private:
    void cacheRows(int, int) const;
    QString fetchRow(int) const;

    mutable QContiguousCache<QString> m_rows;
    const int m_count;
};

#endif
#include "randomlistmodel.h"
#include 
#include 
static const int bufferSize(500);
static const int lookAhead(100);
static const int halfLookAhead(lookAhead/2);

RandomListModel::RandomListModel(QObject *parent)
: QAbstractListModel(parent), m_rows(bufferSize), m_count(10000)
{
}

RandomListModel::~RandomListModel()
{
}

int RandomListModel::rowCount(const QModelIndex &) const
{
    return m_count;
}

/*重写QAbstractListModel获取数据方法data();
  根据行号将内存中的数据加载到在缓存队列,最后在缓存队列中取值
  获得行之后,确定该行是否在连续缓存的当前范围内。
//! [0]

QVariant RandomListModel::data(const QModelIndex &index, int role) const
{
    qDebug() << "data" << index;
    if (role != Qt::DisplayRole)
        return QVariant();

    int row = index.row();

    if (row > m_rows.lastIndex()) {
        if (row - m_rows.lastIndex() > lookAhead)
            cacheRows(row-halfLookAhead, qMin(m_count, row+halfLookAhead));
        else while (row > m_rows.lastIndex())
            m_rows.append(fetchRow(m_rows.lastIndex()+1));
    } else if (row < m_rows.firstIndex()) {
        if (m_rows.firstIndex() - row > lookAhead)
            cacheRows(qMax(0, row-halfLookAhead), row+halfLookAhead);
        else while (row < m_rows.firstIndex())
            m_rows.prepend(fetchRow(m_rows.firstIndex()-1));
    }

    return m_rows.at(row);
}

void RandomListModel::cacheRows(int from, int to) const
{
    for (int i = from; i <= to; ++i)
        m_rows.insert(i, fetchRow(i));
}
//![0]

//![1]
QString RandomListModel::fetchRow(int position) const
{
    return QString::number(QRandomGenerator::global()->bounded(++position));
}
//![1]

mian.cpp

#include "randomlistmodel.h"
#include 
#include 

int main(int c, char **v)
{
    QApplication a(c, v);

    QListView view;
    view.setUniformItemSizes(true);
    view.setModel(new RandomListModel(&view));
    view.show();

    return a.exec();
}

你可能感兴趣的:(《Qt,项目实战经历全记录》,缓存,qt,大量数据展示)