Qt Learning Path: storage container

Source: Internet
Author: User

 

Storage containers (containers), sometimes referred to as collections (collections), are the ability to store other specific types of objects in memory, usually some commonly used data structures, typically in the form of generic template classes. C + + provides a complete set of solutions, as part of the standard Template Library, commonly called STL.

Qt provides a different set of template-based container classes. These container classes are generally lighter, safer, and easier to use than STL. If you are not familiar with STL, or prefer Qt-style APIs, then you should choose to use these classes. Of course, you can also use the STL container in Qt, without any problems.

The purpose of this chapter is to allow you to choose which container to use instead of telling you which functions are in this class. This question can be found in the documentation for a clearer answer.

Qt's container classes do not inherit QObject , providing implicit data sharing, immutable features, and optimized for speed, with low memory consumption, and so on. Another important point is that they are thread-safe. These container classes are platform-independent, that is, they do not have different implementations due to different compilers, and implicit data sharing, sometimes referred to as copy-on-write, is a technique that allows the use of pass-through parameters in container classes without any additional performance penalty. Traversal is an important operation of a container class. The Qt container class provides a Java-like Walker syntax, as well as an STL-like Walker syntax that allows users to choose their own custom encoding. A Java-style walker is easier to use than a high-level function, while an STL-style walker is more efficient and can support both Qt and STL's general-purpose algorithms. Finally, in some embedded platforms, the STL is often unavailable, and you can only use the container classes provided by Qt, unless you want to create them yourself. Incidentally, in addition to the Walker, Qt also provides its own foreach syntax (c + + 11 also provides a similar syntax, but differs, as described here in the Foreach Loop section).

Qt provides a sequential storage container: QList , QLinkedList , QVector , QStack and QQueue . For the vast majority QList of applications, it is the best choice. Although it is a list based on an array implementation, it provides quick forward and backward append operations. If you need a linked list, you can use it QLinkedList . If you want all elements to occupy contiguous address space, you can choose QVector . The QStack and QQueue then are LIFO and FIFO.

Qt also provides associative containers: QMap ,, QMultiMap QHash , QMultiHash and QSet . A container with the word "Multi" supports associating multiple values on a key. The "hash" container provides a faster lookup based on a hash function, rather than a hash container, which is an ordered set based on a binary search.

Two other exceptions: QCache and QContiguousCache provide efficient hash lookups in a limited cache space.

We summarize the various container classes provided by Qt as follows:

  • QList<T>: This is the most common container class that has been provided so far. It stores the object of the given type T as a list, associated with an integral type index. QListuse an array implementation internally, providing fast, index-based access. We can use QList::append() and QList::prepend() add elements to the tail or head of the list, or you can QList::insert() insert them in the middle. QListThis modification is optimized for this type of operation compared to other container classes. QStringListinherit from QList<QString> .
  • QLinkedList<T>: Similar to QList , except that it is a random access using a walker rather than an integer index. For inserting large amounts of data in the middle, it performs better QList . There is also a better ergodic semantics (as long as the data element exists, QLinkedList the walker points to a legitimate element, in contrast, when inserting or deleting data, QList the Walker points to an illegal value).
  • QVector<T>: Used to store a series of values of a given type in contiguous extents of memory. Inserting data in the head or in the middle can be very slow because it can cause large amounts of data to move in memory.
  • QStack<T>: This is a QVector subclass that provides last-in, first-out (LIFO) semantics. Compared QVector to this, it provides additional functions: push() , pop() and top() .
  • QQueue<T>: This is QList the subclass that provides FIFO semantics. Compared QList to this, it provides additional functions: enqueue() , dequeue() and head() .
  • QSet<T>: Provides a single-valued set of mathematics above, with fast lookup performance.
  • QMap<Key, T>: A dictionary data structure (associative array) is provided, associating the key of the same type key with the value of T. Typically, each key is associated with a value. QMapstores data in the order of keys, and provides better performance if the order is irrelevant QHash .
  • QMultiMap<Key, T>: This is a QMap subclass that provides a multivalued mapping: A key can be associated with multiple values.
  • QHash<Key, T>: This similar QMap interface is almost the same, but provides faster lookups. QHashstores data in alphabetical order.
  • QMultiHash<Key, T>: This is a QHash subclass that provides a multivalued hash.

All containers can be nested. For example, QMap<QString, QList<int> > a map whose key is a QString type, the value is a type, that is QList<int> , each value can store more than one int. It is important to note that the C + + compiler will use two consecutive > as the input redirection operator, so there must be a space in the middle of two > here.

The data that can be stored in the container must be an assignable data type . The so-called assignable data type is the type with a default constructor, copy constructor, and assignment operator. The vast majority of data types, including basic types, such as int and double, pointers, and Qt data types, such as, QString QDate and QTime , are assignable data types. However, QObject and its subclasses ( QWidget , and QTimer so on) are not. In other words, you cannot use QList<QWidget> this container because QWidget the copy constructor and assignment operators are not available. If you need this type of container, you can only store its pointer, that is QList<QWidget *> .

If you want to use QMap or QHash , the type that is the key must provide additional auxiliary functions. QMapThe keys must provide operator<() overloads, QHash the keys must provide operator==() overloads, and a name is qHash() the global function.

As an example, we consider the following code:

123456< /td> struct Movie {     int id     qstring Title     qdate releaseDate< Span class= "Crayon-sy"; }

As a struct, we use it as a pure data class. This class has no additional constructors, so the compiler generates a default constructor for us. At the same time, the compiler generates default copy constructors and assignment operators. This satisfies the criteria for placing it in the container class store:

1 Qlist<Movie> movs;

The Qt container class can be QDataStream accessed directly using the. At this point, the type stored in the container must also be used QDataStream for storage. This means that we need overloads operator<<() and operator>>() operators:

1234567891011121314151617 qdatastream &operator<<(qdatastream &out, Const movie &movie) { out << (quint32)movie. ID << movie. Title << movie. ReleaseDate; return out ; }qdatastream &operator>>(qdatastream &in, movie &movie) { quint32 ID; qdate date; In >> ID >> movie. Title >> date; movie. ID = (int)ID; movie. ReleaseDate = date; return in ; }

Based on the relevant contents of data structure, it is necessary to quantify the complexity of the algorithm of these container classes. The complexity of the algorithm is concerned with how fast (or how slow) each function of a container is when the amount of data grows. For example, QLinkedList inserting data into the middle is a fairly quick operation and has nothing to do with the QLinkedList amount of data that has been stored in. On the other hand, if a QVector large amount of data has been saved, QVector inserting data into the middle is very slow because in memory half of the data must be moved. To describe the complexity of the algorithm, we introduce the O notation (capital letter O, read "Big O"):

    • Constant time: O (1). If the run time of a function is independent of the amount of data in the container, we say that this function is constant time. QLinkedList::insert()is the constant time.
    • Logarithmic time: O (log n). If the run time of a function is a logarithmic relationship of the amount of data in the container, we say that this function is logarithmic time. qBinaryFind()is the logarithmic time.
    • Linear time: O (n). If the run time of a function is a linear relationship of the amount of data in the container, that is, directly related to the quantity, we say that this function is limited to the line time. QVector::insert()is the linear time.
    • Linear logarithmic time: O (n log n). The linear logarithm time is slower than the linear time, but faster than the square time.
    • Square Time: O (n²). The square relationship between the squared time and the container data quantity.

Based on the above representation, let's look at the algorithm complexity of the Qt sequential container:

Find Insert Add ahead Rear Append
QLinkedList<T> O (N) O (1) O (1) O (1)
QList<T> O (1) O (N) Statistics O (1) Statistics O (1)
QVector<T> O (1) O (N) O (N) Statistics O (1)

In the above table, the so-called "statistics" means data in statistical sense. For example, "statistic O (1)" means that if it is called only once, its run time is O (n), but if it is called multiple times (for example, n times), the average time is O (1).

The following table is the algorithm complexity of the associated container:

o (log n)
&NBSP; find key insert
average worst average worst
qmap<key, t> O (log n) O (log n) O (log n) O (log n)
Qmultimap<key, t> O (log n) O (log n) O (log n)
qhash<key, t> Statistics O (1) o (n) O ( 1) statistics O (n)
qset<key, t> Statistics O (1) O (n) O (1) statistics O (n)

QVector, QHash and QSet the head add is statistically significant O (log n). However, by giving the number of elements before inserting, QVector::reserve() QHash::reserve() and QSet::reserve() , we can reduce the complexity to O (1). We will discuss the issue in detail below.

QVector<T>, QString and QByteArray store data in contiguous memory space. QList<T>maintains an array of pointers to its data, providing fast index-based access (if T is a pointer type, or other type that is the same size as the pointer), the Qlist internal array is the actual value, not its pointer. QHash<Key, T>maintains a hash table with the same size as the amount of data in the hash. To avoid reassigning data space each time the data is inserted, these classes provide the data bits of the extra actual value.

We use the following code to understand this algorithm:

123456789 QString onlyletters(const QString &in) { QString out;     for (int j Span class= "Crayon-o" >= 0; j < in. Size () ; ++j) { /span> if (in[J]. Isletter()) Out + = in [J];     } return out ; }

We create a string that appends one character at a time. Suppose we need to append 15,000 characters. In the process of running the algorithm, when the following space is reached, the memory space will be re-allocated, there will be 18 times: 4,8,12,16,20,52,116,244,500,1012,2036,4084,6132,8180,10228,12276,14324, 16372. Finally, there are 16,372 Unicode characters in this out object, 15,000 of which have actual data.

The allocation data above is somewhat strange, in fact it is rule-based:

    • QStringAllocate 4 characters at a time until 20 is reached.
    • Between 20 and No. 4084, each allocation is approximately one-fold. To be precise, the power minus 12 of the next 2 is allocated each time. (Some memory allocators have very poor performance when allocating a power of 2, because they will consume certain bytes to make a reservation)
    • From 4084 onwards, allocate 2048 characters (4096 bytes) at a time. This is of particular significance because the modern operating system does not replicate the entire data when it allocates a cache; The physical memory pages are simply reordered, and only the first and last pages of data are copied.

QByteArrayand QList<T> The actual algorithm QString is very similar.

Similar algorithms are used for data types that can be used memcry() (including basic C + + type, pointer type, and QT shared classes) functions to move through memory, and QVector<T> for data types that can only be moved using copy constructors and destructors, another set of algorithms is used. Because the latter consumes more, QVector<T> it reduces the amount of extra memory that is allocated each time you go out of space.

QHash<Key, T>is a completely different form. QHashThe internal hash list increases the power of 2 each time, and each time it is incremented, all data is reassigned to the new bucket, with the formula qHash(key) % QHash::capacity() ( QHash::capacity() that is, the number of buckets). The same algorithm applies to QSet<T> and QCache<Key, T> . If you do not understand the concept of "bucket", please consult the relevant content of the data structure.

For most applications. The Qt default growth algorithm is sufficient. If you need additional control,,,, QVector<T> QHash<Key, T> QSet<T> QString and QByteArray provide a series of functions to detect and specify exactly how much memory to allocate:

    • capacity(): Returns the number of elements that are actually allocated memory (for QHash sum QSet , the number of buckets in the hash table)
    • reserve(size): Explicitly pre-allocates memory for a specified number of elements.
    • squeeze(): Frees up memory space that does not require real storage of data.

If you know how much data the container has, you can reduce the reserve() memory footprint by calling the function. If all the data has been stored in the container, you can call the squeeze() function to release any unused pre-allocated space.

Qt Learning Path: storage container

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.