Qt中的常用容器类_qt exe应用容器(1)

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)

如果你需要这些资料,可以戳这里获取

下面的代码展示了,使用Java风格的迭代器遍历一个QList的典型做法:

  QList<QString> list;
  list << "A" << "B" << "C" << "D";

  QListIterator<QString> i(list);
  while (i.hasNext())
      qDebug() << i.next();

该代码的执行原理是:将要遍历的QList传给QListIterator的构造函数。此时,迭代器就指向了链表中第一个元素的前面,即"A"的前面。接着,我们调用hasNext()方法来判断在当前迭代器后面是否有一个元素。如果有,我们就调用next()函数来跳过那个元素。并且,next()函数会返回它跳过的那个元素。在这个例子中就是返回一个QString字符串。

下面的代码展示了怎么从后向前遍历一个QList:

  QListIterator<QString> i(list);
  i.toBack();
  while (i.hasPrevious())
      qDebug() << i.previous();

该代码和向前遍历的代码类似,除了我们在一开始调用了toBack()函数将迭代器移到最后一个元素的后面。

下面的图示说明了电影next() 和 previous()的作用:

下表中列出了QListIterator类的API及其作用:

toFront()移动迭代器到第一个元素之前
toBack()移动迭代器到最后一个元素之后
hasNext()如果迭代器还未遍历到列表的最后,返回true
next()返回下一个元素,并将迭代器向前移动一个位置。
peekNext()返回下一个元素,不移动迭代器。
hasPrevious()如果迭代器还未遍历到列表的前端,返回true。
previous()返回前一个元素,并将迭代器向后移动一个位置。
peekPrevious()返回前一个元素,不移动迭代器。

另外,上面我们就说过,QListIterator是只读迭代器,所以,我们无法使用该迭代器在遍历的过程中进行插入或删除操作。要使用这种功能,必须使用QMutableListIterator。下面的例子展示了使用QMutableListIterator来删除QList中所有的奇数:

  QMutableListIterator<int> i(list);
  while (i.hasNext()) {
      if (i.next() % 2 != 0)
          i.remove();
  }

在上面的循环中,每次循环都调用了next()函数。这会跳过列表中的下一个元素。remove()函数会从链表中删除我们之前跳过的那个元素。并且,remove()函数不会使迭代器失效,我们可以安全的继续使用它。对于,从后向前遍历也是一样,如下代码所示:

  QMutableListIterator<int> i(list);
  i.toBack();
  while (i.hasPrevious()) {
      if (i.previous() % 2 != 0)
          i.remove();
  }

如果我们只是想修改一个现存的元素,我们可以使用setValue()函数。如下面的代码,我们用128替换容器中大于128的元素:

  QMutableListIterator<int> i(list);
  while (i.hasNext()) {
      if (i.next() > 128)
          i.setValue(128);
  }

类似于remove()函数,setValue()也是工作在我们刚跳过的元素上。如果我们是向前遍历,该元素就是当前迭代器之前的那个元素;如果我们是向后遍历,该元素就是当前迭代器之后的那个元素。

其实,next()函数会返回一个元素的非常量引用。所以,对应简单的操作,我们不需要调用setValue()函数,而是直接进行相应修改即可。如下代码:

  QMutableListIterator<int> i(list);
  while (i.hasNext())
      i.next() *= 2;

我们上面提到过,QLinkedList,QVector,QSet的迭代器操作和QList完全一下。那么,下面我们就来看一下QMapIterator,因为该迭代器是工作在key-value对上,所以和上面讲的有点不同。

类似于QListIterator,QMapIterator也提供了toFront(),toBack(),hasNext(),next(),peekNext(),hasPrevious(),peekPrevious()。至于具体的key和value,我们可以调用key() 和 value() 函数,从next(),peekNext(),previous()或者peekPrevious()返回的对象中提取。

下面的例子中,我们从map中删除所有capital以"City" 结尾的(capital,country)对:

  QMap<QString, QString> map;
  map.insert("Paris", "France");
  map.insert("Guatemala City", "Guatemala");
  map.insert("Mexico City", "Mexico");
  map.insert("Moscow", "Russia");
  ...

  QMutableMapIterator<QString, QString> i(map);
  while (i.hasNext()) {
      if (i.next().key().endsWith("City"))
          i.remove();
  }

其实,QMapIterator也提供了相应的key() 和 value() 函数,可以直接作用于迭代器本身,返回上次跳过的元素的键和值。例如,下面的代码将QMap的元素拷贝到QHash:

  QMap<int, QWidget *> map;
  QHash<int, QWidget *> hash;

  QMapIterator<int, QWidget *> i(map);
  while (i.hasNext()) {
      i.next();
      hash.insert(i.key(), i.value());
  }

如果你想迭代所有具有特定值的元素,可以使用findNext()或findPrevious()。在下面的例子中,我们从容器中删除具有特定值的所有项:

  QMutableMapIterator<int, QWidget *> i(map);
  while (i.findNext(widget))
      i.remove();

STL风格的迭代器:
STL风格的迭代器,在Qt 2 中就存在了。它们兼容于Qt和STL的通用算法,并且在访问速度上进行了优化。

同样,每一种容器也都提供了两种类型的STL风格迭代器:只读迭代器和读写迭代器。我们应尽量使用只读迭代器,因为它们更快。

我们同样用一张表来列举每一种STL风格的迭代器:

ContainersRead-only iteratorRead-write iterator
QList, QQueueQList::const_iteratorQList::iterator
QLinkedListQLinkedList::const_iteratorQLinkedList::iterator
QVector, QStackQVector::const_iteratorQVector::iterator
QSetQSet::const_iteratorQSet::iterator
QMap<Key, T>, QMultiMap<Key, T>QMap<Key, T>::const_iteratorQMap<Key, T>::iterator
QHash<Key, T>, QMultiHash<Key, T>QHash<Key, T>::const_iteratorQHash<Key, T>::iterator

STL迭代器的API在每一个类中也都有详细的说明。比如,++运算符会将迭代器前进到下一个元素,*运算符返回迭代器所指向的元素。事实上,对QVector和QStack来说,由于它们的元素都是存储在连续的内存中,所以它们的迭代器类型就是T*,它们的只读迭代器类型就是const T*。下面,我们还是以QList和QMap为例还说明stl风格的迭代器的使用方法。

下面的例子代码,是使用STL风格的迭代器遍历QList的典型方式:

  QList<QString> list;
  list << "A" << "B" << "C" << "D";

  QList<QString>::iterator i;
  for (i = list.begin(); i != list.end(); ++i)
      *i = (*i).toLower();

不同于Java风格的迭代器,STL风格的迭代器直接指向具体的元素。容器的begin() 方法返回一个指向容器中第一个元素的迭代器。end() 方法返回一个指向容器中最后一个元素的下一个位置的迭代器。end()标识了一个无效的位置,绝不应该对它解引用。它经常被用来在一个循环中做为结束条件。如果链表为空,begin()就等于end()。下面的图示说明了在一个容器中对STL风格的迭代器来说,有效的迭代器位置:

同样,使用STL风格的迭代器做反向遍历的代码如下:

  QList<QString> list;
  list << "A" << "B" << "C" << "D";

  QList<QString>::reverse_iterator i;
  for (i = list.rbegin(); i != list.rend(); ++i)
      *i = i->toLower();
  }

到目前为止,我们在代码中都是使用一元运算符*来提取某个迭代器位置的元素内容,然后调用QString;:toLower()。其实,大部分c++编译器还允许我们使用i->toLower()的形式。对于只读迭代器,可以使用const_iterator,例如:

  QList<QString>::const_iterator i;
  for (i = list.constBegin(); i != list.constEnd(); ++i)
      qDebug() << *i;

接下来,我们也用一张表来总结一下stl风格的迭代器的相关操作:

*i返回当前元素
++i步进迭代器到下一个元素位置
i += n将迭代器向前步进n个元素
–i步进迭代器到前一个位置
i -= n将迭代器向前步进n个位置
i - j返回 迭代器 i 和 j之间的元素个数

对于++和–操作,既支持前++,也支持后++,–也一样。至于这两者的区别,我相信大家都能理解,在此就不解释了。

对于非const迭代器类型,*运算符返回的值可以被当做左值来使用。

对于QMap和QHash来说,*运算符返回一个元素的value部分。如果你想获得key,可以在迭代器上调用key() 方法。而处于对称性,迭代器还提供了value() 方法来获得value()值。例如,下面的代码说明了怎么打印出QMap中的所有元素:

  QMap<int, int> map;
  ...
  QMap<int, int>::const_iterator i;
  for (i = map.constBegin(); i != map.constEnd(); ++i)
      qDebug() << i.key() << ':' << i.value();

并且,由于 “隐式共享”,一个函数返回一个容器的代价并不高。在Qt的API中,包含了很多返回QList或QStringList的函数,比如QSplitter::sizes()。如果你想使用STL风格的迭代器来迭代这些容器,你应该先拿到该容器的一份拷贝,然后遍历这份拷贝。例如:

  // RIGHT
  const QList<int> sizes = splitter->sizes();
  QList<int>::const_iterator i;
  for (i = sizes.begin(); i != sizes.end(); ++i)
      ...

  // WRONG
  QList<int>::const_iterator i;
  for (i = splitter->sizes().begin();
          i != splitter->sizes().end(); ++i)
      ...

foreach 关键字
如果你只是想顺序的变量容器中的所以元素,可以使用Qt的foreach关键字。这个关键字是Qt特定的,是使用预处理器实现的。

它的语法是:foreach(variable, container) statement。例如,下面的代码说明了怎么使用foreach来迭代QLinkedList:

  QLinkedList<QString> list;
  ...
  QString str;
  foreach (str, list)
      qDebug() << str;

使用foreach的代码,通常都会比使用迭代器写出的代码更短:

  QLinkedList<QString> list;
  ...
  QLinkedListIterator<QString> i(list);
  while (i.hasNext())
      qDebug() << i.next();

如果容器中的数据类型不包括逗号,那么,我们还可以将遍历容器所用的变量定义在foreach内部。如下所示:

  QLinkedList<QString> list;
  ...
  foreach (const QString &str, list)
      qDebug() << str;

同样,类似于c++的for循环,当有多条语句时,也可以使用花括号和break关键字:

  QLinkedList<QString> list;
  ...
  foreach (const QString &str, list) {
      if (str.isEmpty())
          break;
      qDebug() << str;
  }

而对于QMap和QHash来说,foreach会访问其中存储的key-value对的value部分。如果你想同时获得key和value,可以使用迭代器,或者先获得其中的key,在通过key取到对应的值。如下代码所示:

  QMap<QString, int> map;
  ...
  foreach (const QString &str, map.keys())
      qDebug() << str << ':' << map.value(str);

而对于多值关联的map来说,可以使用两个foreach,如下方式访问:

  QMultiMap<QString, int> map;
  ...
  foreach (const QString &str, map.uniqueKeys()) {
      foreach (int i, map.values(str))
          qDebug() << str << ':' << i;
  }

在进入一个foreach循环时,Qt会自动拿到容器的一个拷贝。所以,如果你在foreach的过程中,修改了容器,并不会影响这个循环。

而因为foreach会创建出一份容器的拷贝,所以使用一个非常量引用并不会使你能够修改原始容器。而仅仅会影响到拷贝,这可能不是你想要的结果。

所以,相对于Qt的foreach,一个可选的方案是C++11中的基于范围的for循环。但是,要注意的是,基于范围的for循环可以会强制一个Qt容器脱离隐式共享,而foreach不会。但是,使用foreach总是会拷贝容器,这个代价对STL的容器来说,通常是昂贵的。所以,一般,我们可以对Qt容器使用foreach关键字,而对于STL的容器使用基于范围的for循环。而除了上面的foreach之外,Qt还为无限循环提供了一个伪关键字:forever。使用如下:

  forever {
      //一直执行的代码
  }

当然,如果你担心这些Qt特定的关键字会导致名称空间的污染,也可以禁用跌这些宏。只需在.pro文件中添加如下一句即可:

CONFIG += no_keywords

其他的类容器类
Qt中包含三个模板类,其在某些方面类似于容器。但这些类不提供迭代器,也不能用于foreach关键字。

  • QVarLengthArray<T, Prealloc>:该类提供了一个低级的变长数组。在某些非常看重访问速度的情况下,可以使用该类替代QVector。
  • QCache<Key, T>:该类提供了一个存储key-value对的缓存
  • QContiguousCache:该类提供了一种高效的缓存数据的方式,其是使用连续的内存进行访问。
  • QPair<T1, T2>:用来存储元素对。

算法复杂度
算法复杂度是当容器中的元素增多时每一个函数的运行速度是多块或多慢。例如,在QLinkedList的中间插入一个元素是一个非常快速的操作,而不管当前链表中有多少元素。另一方面,在QVector的中间插入一个元素效率就是非常低下的,特别是当QVector中已经有了大量的元素,因为,这个操作会导致QVector中一半的元素都要在内存中移动一个位置。

收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。
img
img

如果你需要这些资料,可以戳这里获取

需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

插入一个元素效率就是非常低下的,特别是当QVector中已经有了大量的元素,因为,这个操作会导致QVector中一半的元素都要在内存中移动一个位置。

收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。
[外链图片转存中…(img-Ri791ogL-1715898569005)]
[外链图片转存中…(img-SWuzLFM0-1715898569005)]

如果你需要这些资料,可以戳这里获取

需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 10
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值