首页 > 代码库 > 第37课 深度解析QMap与QHash

第37课 深度解析QMap与QHash

1. QMap深度解析

(1)QMap是一个以升序顺序存储键值对的数据结构

  ①QMap原型为 class QMap<K, T>模板

  ②QMap中的键值对根据Key进行了排序

  ③QMap中的Key类型必须重载operator< 。(即“小于”操作符)

(2)QMap使用示例1

QMap<QString, int> map;//注意插入时是无序的map.insert("key 2", 2);map.insert("key 0", 0);map.insert("key 1", 1);for(int i=0; i<3; i++){    //通过键取值    qDebug() << map.value("key " + QString::number(i));}//注意map中的key会被升序排序QList<QString> list = map.keys();for(int i=0; i<list.count(); i++){    qDebug() << list[i];}

(3)QMap使用示例2:用数组方式迭代器方式访问

QMap<QString, int> map;//以数组方式存取,注意输入时为无序的map["key 2"] = 2;map["key 0"] = 0;map["key 1"] = 1;for(int i=0; i<3; i++){    //以数组方式取值    qDebug() << map["key " + QString::number(i)];}//it可看到一个指针,指向第1个元素之前。QMapIterator<QString, int> it(map);while(it.hasNext()){    it.next();    qDebug() << it.key() << " : " << it.value();}

(4)QMap的注意事项

  ①通过Key获取Value时:

    A.当Key存在时:返回对应的Value;

    B.当Key不存在时:返回值类型所对应的“零”值

  ②插入键值对时

    A.当Key存在时更新Value的值

    B.当Key不存在时插入新的键值对

【编程实验】QMap使用体验

#include <QCoreApplication>#include <QMap>#include <QDebug>int main(int argc, char *argv[]){    QCoreApplication a(argc, argv);    QMap<QString, int> map;    //插入键值对,注意,插入操作是无序的,存入后会被自动升序排序。    map.insert("key 2", 2);    map.insert("key 1", 1);    map.insert("key 0", 0);    //取出所有的键(经过排序的)    QList<QString> kList = map.keys();    for(int i=0; i<kList.count(); i++)    {        qDebug() << kList[i];//注意,输出元素是升序排列的    }    //取出所有的值(根据key排序过的顺序)    QList<int> vList = map.values();    for(int i=0; i<vList.count(); i++)    {        qDebug() << vList[i];//注意,输出元素是根据key排序过的    }    //通过迭代器遍历    QMapIterator<QString, int> it(map); //it指针第1个元素之前的位置    while(it.hasNext())    {        it.next();        qDebug() << it.key() << " : " << it.value();    }    return a.exec();}/*输出结果:"key 0""key 1""key 2"012"key 0"  :  0"key 1"  :  1"key 2"  :  2*/

2. QHash深度解析

(1)QHash是Qt中的哈希数据结构

  ①QHash原型为class QHash<K, T>模板

  ②QHash中的键值对在内部无序排列。注意QMap是升序排序的

  ③QHash的Key类型必须重载operator==。(即“相等”操作符)

  ④QHash中的Key对象必须重载全局哈希函数qHash()

(2)QHash使用示例

QHash<QString, int> hash;//以数组方式存取,注意输入时为无序的hash["key 2"] = 2;hash["key 0"] = 0;hash["key 1"] = 1;QHash<QString, int>::const_iterator i; //const_iterator为内部类for(int i=hash.constBegin(); i!= hash.constEnd(); ++i){    qDebug() << i.key() << "" << i.value();}//it可看到一个指针,指向第1个元素之前。QMapIterator<QString, int> it(map);while(it.hasNext()){    it.next();    qDebug() << it.key() << " : " << it.value();}

【编程实验】QHash使用体验

#include <QCoreApplication>#include <QHash>#include <QDebug>int main(int argc, char *argv[]){    QCoreApplication a(argc, argv);    QHash<QString, int> hash;    //插入键值对,注意,插入操作是无序的,但存入后不会自动排序。    hash.insert("key 2", 2);    hash.insert("key 0", 0);    hash.insert("key 1", 1);    //取出所有的键    QList<QString> kList = hash.keys();    for(int i=0; i<kList.count(); i++)    {        qDebug() << kList[i];    }    //取出所有的值    QList<int> vList = hash.values();    for(int i=0; i<vList.count(); i++)    {        qDebug() << vList[i];    }    hash["key 4"] = 4; //通过数组方式存入键值对    //通过迭代器遍历    QHash<QString, int>::const_iterator it;    for(it = hash.constBegin(); it != hash.constEnd(); ++it)    {        qDebug() << it.key() << " : " << it.value();    }    return a.exec();}/*输出结果:"key 2""key 0""key 1"201"key 2"  :  2"key 4"  :  4"key 0"  :  0"key 1"  :  1*/

(3)QMap和QHash接口的比较

 

QMap

QHash

接口

接口相同,可直接替换使用

查找速度

较慢

占用存储空间

较小

元素存储

以Key的升序存领储

任意的方式

键类型必须重载的函数

Operator<()函数

Operator==()和qHash(Key)函数

【编程实验】文本编辑器程序中的文件后缀名映射

//处理后缀的代码

QString MainWindow::showFileDialog(QFileDialog::AcceptMode mode, QString title){    QString ret = "";    QFileDialog fd(this);    QStringList filters;    QMap<QString, QString> map;    const char* filterArray[][2] =    {        {"Text(*.txt)",    ".txt"},        {"All Files(*.*)", "*"   },        {NULL,             NULL}    };    for (int i=0; filterArray[i][0] != NULL; i++)    {        filters.append(filterArray[i][0]);        map.insert(filterArray[i][0], filterArray[i][1]);    }    fd.setWindowTitle(title);    fd.setAcceptMode(mode); //QFileDialog::AcceptOpen或AcceptSave    fd.setNameFilters(filters);    if(mode == QFileDialog::AcceptOpen)    {        fd.setFileMode(QFileDialog::ExistingFile); //打开文件必须存在!    }    if(fd.exec() == QFileDialog::Accepted)    {        ret = fd.selectedFiles()[0];        //Qt5中ret返回的是完整的路径名,含后缀。因此,后面的if块可省略,但Qt4可能        //会返回不带后缀的文件名,当保存文件时,须手动加上去。        if(mode == QFileDialog::AcceptSave)        {            QString postfix = map[fd.selectedNameFilter()];            if((postfix != "*") && !ret.endsWith(postfix))            {                ret = ret + postfix;            }        }    }    return ret;}

 //完整的MainWindowSlots.cpp代码

//该文件MainWindowSlots.cpp与MainWindowUI.cpp的分离//体现了界面和功能代码分离的思想#include "MainWindow.h"#include <QMessageBox>#include <QFile>#include <QTextStream>#include <QMap>#include <QDebug>void MainWindow::showErrorMessage(QString message){    QMessageBox msg(this);    msg.setWindowTitle("Erro");    msg.setText(message);    msg.setIcon(QMessageBox::Critical);    msg.setStandardButtons(QMessageBox::Ok);    msg.exec();}int MainWindow::showQueryMessage(QString message){    QMessageBox msg(this);    msg.setWindowTitle("Query");    msg.setText(message);    msg.setIcon(QMessageBox::Question);    msg.setStandardButtons(QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);    return msg.exec();}QString MainWindow::showFileDialog(QFileDialog::AcceptMode mode, QString title){    QString ret = "";    QFileDialog fd(this);    QStringList filters;    QMap<QString, QString> map;    const char* filterArray[][2] =    {        {"Text(*.txt)",    ".txt"},        {"All Files(*.*)", "*"   },        {NULL,             NULL}    };    for (int i=0; filterArray[i][0] != NULL; i++)    {        filters.append(filterArray[i][0]);        map.insert(filterArray[i][0], filterArray[i][1]);    }    fd.setWindowTitle(title);    fd.setAcceptMode(mode); //QFileDialog::AcceptOpen或AcceptSave    fd.setNameFilters(filters);    if(mode == QFileDialog::AcceptOpen)    {        fd.setFileMode(QFileDialog::ExistingFile); //打开文件必须存在!    }    if(fd.exec() == QFileDialog::Accepted)    {        ret = fd.selectedFiles()[0];        //Qt5中ret返回的是完整的路径名,含后缀。因此,后面的if块可省略,但Qt4可能        //会返回不带后缀的文件名,当保存文件时,须手动加上去。        if(mode == QFileDialog::AcceptSave)        {            QString postfix = map[fd.selectedNameFilter()];            if((postfix != "*") && !ret.endsWith(postfix))            {                ret = ret + postfix;            }        }    }    return ret;}void MainWindow::preEditorChanged(){    if (m_isTextChanged)    {        int r = showQueryMessage("Do you want to save the changes to file?");        switch(r)        {        case QMessageBox::Yes:            saveCurrentData(m_filePath);            break;        case QMessageBox::No:            m_isTextChanged = false;            break;        case QMessageBox::Cancel:            break;        }    }}void MainWindow::onFileNew(){    preEditorChanged();    if(!m_isTextChanged)    {        mainEditor.clear();        setWindowTitle("NotePad - [ New ]");        m_filePath = "";        m_isTextChanged = false;    }}void MainWindow::onFileOpen(){    preEditorChanged();    if( !m_isTextChanged)    {        QString path = showFileDialog(QFileDialog::AcceptOpen, "Open");        if( path != "")        {            QFile file(path);            if (file.open(QIODevice::ReadOnly | QIODevice::Text))            {                mainEditor.setPlainText(QString(file.readAll()));                file.close();                m_filePath = path; //记录当前打开的文件路径和文件名                m_isTextChanged = false;                setWindowTitle("NotePad - [" + m_filePath + "]");            }            else            {                showErrorMessage(QString("Open file Error!\n\n") + "\"" + path + "\"");            }        }    }}QString MainWindow::saveCurrentData(QString path, QString title){    QString ret = path;    if (ret =="")    {        //执行下面语句时,用户可以点击“取消”,此时ret返回""        ret = showFileDialog(QFileDialog::AcceptSave, title);    }    if (ret != "")    {        QFile file(ret);        if (file.open(QIODevice::WriteOnly | QIODevice::Text))        {            QTextStream out(&file);            out << mainEditor.toPlainText();            file.close();            setWindowTitle("NotePad - [" + ret + "]");            m_isTextChanged = false; //己保存        }        else        {            showErrorMessage(QString("Save file Error!\n\n") + "\"" + m_filePath + "\"");            ret =""; //保存失败时        }    }    return ret;}void MainWindow::onFileSave(){    QString path = saveCurrentData(m_filePath, "Save");    if( path != "" )    {        m_filePath = path;    }}void MainWindow::onFileSaveAs(){    QString path = saveCurrentData(m_filePath, "Save As");    if( path != "" )    {        m_filePath = path;    }}void MainWindow::onTextChanged(){    if( !m_isTextChanged )    {        setWindowTitle("*" + windowTitle());    }    m_isTextChanged = true;    //qDebug()<< "onTextChanged()";}

3. 小结

(1)Qt中提供了用于存储键值对的类模板

(2)QHash和QMap遵循相同的使用接口

(3)QHash的查找速度快于QMap

(4)QMap需要的内存空间低于QHash

(5)QHash对于Key类型的要求高于QMap。(QHash要求key类型操作两个操作符函数)

第37课 深度解析QMap与QHash