Qt 容器类整理与使用

Qt提供了哪些容器类

Qt 提供了丰富的容器类,这些容器类主要用于存储和管理数据,按照其内部组织结构和功能特性,大致可分为顺序容器和关联容器两大类:
顺序容器

  1. QList - 动态数组,支持快速的头部和尾部插入删除操作,以及通过索引访问元素。
  2. QVector - 类似于QList,但内部实现保证了元素在内存中连续存储,对于大数据量并且频繁随机访问时,可能有更好的性能表现。
  3. QLinkedList - 双向链表,支持高效地在列表中间进行插入和删除操作,但不支持通过索引直接访问元素。
  4. QStack - 后进先出(LIFO)栈容器。
  5. QQueue - 先进先出(FIFO)队列容器。

关联容器

  1. QMap - 键值对映射容器,键值对按键自动排序。

  2. QHash - 哈希表实现的键值对容器,提供常数时间复杂度的快速查找能力。

  3. QSet - 集合容器,只包含唯一元素,不存储值对应的键。

另外还有一些特殊用途的容器:

  1. QStringList - 字符串列表容器,实际上是QList<QString>的便捷版本,专用于存储字符串列表。

  2. QByteArray - 字节数组容器,用于存储二进制数据。

以上容器类都是模板类,可以根据需要存储任何兼容类型的对象,并且它们在设计上力求简化接口、提高跨平台兼容性和线程安全性。相较于C++标准模板库(STL),Qt的容器类通常被认为更适合于GUI开发环境,尤其是考虑到多线程同步问题时。

QList 详细介绍

QList详细介绍

QList是Qt框架中的一个重要的泛型容器类,它是动态数组的实现,但是它的内部实现并不是简单的连续内存区域,而是根据元素大小和数量选择最优的存储策略(可能是数组或者链表)。这意味着QList能够灵活地应对不同场景下的需求,尤其是在元素较小或者大量元素已预先分配空间的情况下,它会尽可能地利用连续内存来加速随机访问。

特点与功能

  • 模板类:QList是一个模板类,可以存储任何QObject派生类或者其他POD类型的数据。
  • 动态性:QList的大小可以在运行时动态调整,可以随时添加或移除元素。
  • 索引访问:支持通过整数索引访问和修改元素,类似于C++的标准库vector。
  • 快速插入删除:在列表的开头和末尾进行插入和删除操作非常高效,而在列表中间进行插入和删除时,效率取决于具体实现(数组还是链表)。
  • 迭代器:QList也提供了迭代器来进行元素遍历。
  • 内存管理:QList自动管理所存储元素的内存,无需手动分配和释放内存。
  • API :提供了丰富的API,包括但不限于append()prepend()insert()removeAt()takeAt()replace()等。

优缺点

  • 优点
    • 内部优化,适合特定情况下的高效存储和访问。
    • 支持快速的头部和尾部插入删除。
    • 提供了基于索引的操作,直观易用。
    • 自动管理内存,减少了手动内存管理错误的可能性。
  • 缺点
    • 如果QList内部采用链表结构存储,在中间插入或删除元素时,虽然不会像普通数组那样导致大量元素移动,但仍然不如QVector(当元素大小远大于指针大小时)的随机插入删除快。
    • 当元素大小较大时,如果QList选择链表存储而非数组,那么连续访问性能会下降。

使用示例(增删改查)

#include <QList>
#include <QDebug>

// 创建一个存储整数的QList
QList<int> numbers;

// 增加元素
numbers.append(1);
numbers.append(2);
numbers.append(3);
qDebug() << "After appending: " << numbers; // 输出: [1, 2, 3]

// 修改元素
numbers[1] = 10;
qDebug() << "After modifying: " << numbers; // 输出: [1, 10, 3]

// 查询元素
if (numbers.contains(10)) {
    qDebug() << "10 is in the list.";
}

// 删除元素
numbers.removeOne(10); // 删除第一个出现的10
qDebug() << "After removing 10: " << numbers; // 输出: [1, 3]

// 查找并替换元素
int index = numbers.indexOf(3);
if (index != -1) {
    numbers.replace(index, 20); // 将索引处的3替换为20
}
qDebug() << "After replacing: " << numbers; // 输出: [1, 20]

// 在特定位置插入元素
numbers.insert(1, 5);
qDebug() << "After inserting at index 1: " << numbers; // 输出: [1, 5, 20]

以上代码片段演示了如何使用QList进行基本的增删改查操作。同时,还可以使用takeAt()方法移除并返回指定索引的元素,以及其他更多的高级功能,如反转列表、合并两个列表等。

QVector 详细介绍

QVector 详细介绍

QVector 是 Qt 框架中的一个模板类,类似于 C++ STL 中的 std::vector,它提供了一个动态可变大小的数组,允许高效地存储和操作相同类型的数据。QVector 主要用于需要随机访问和连续存储空间的场景,它的内部实现是一个可动态扩展的连续内存区域。

优点:

  1. 快速随机访问:由于数据存储在连续的内存块中,QVector 支持通过索引直接访问元素,具有与静态数组相似的 O(1) 时间复杂度。
  2. 内存管理:自动处理内存分配和释放,能够根据需要动态增长或收缩容量。
  3. 迭代器支持:提供随机访问迭代器,便于遍历和算法操作。
  4. 内置兼容性:作为 Qt 框架的一部分,与其他 Qt 类型如 QList、QSet 等高度集成,且适合信号/槽机制。

缺点:

  1. 插入和删除效率:在中间位置插入或删除元素时,可能导致大量元素移动,因为它是基于连续内存布局的。相比于 QList,QList 使用了链表结构,在中间插入和删除时更为高效。
  2. 内存碎片:随着频繁的插入和删除操作,可能会导致内存空间碎片化,影响性能。

使用示例及增删改查功能演示:

#include <QVector>
#include <QDebug>

int main() {
    // 创建一个空的 QVector
    QVector<int> numbers;

    // 添加元素(增):
    numbers.append(10);
    numbers.append(20);
    numbers.append(30);

    // 插入元素(增):
    numbers.insert(1, 15); // 在索引1处插入15
    qDebug() << "After insertion:" << numbers; // 输出:[10, 15, 20, 30]

    // 修改元素(改):
    numbers[1] = 16;
    qDebug() << "After modification:" << numbers; // 输出:[10, 16, 20, 30]

    // 查找元素:
    if (numbers.contains(16)) {
        qDebug() << "16 is in the vector.";
    }
    
      // 删除最后一个元素
         numbers.pop_back();
    
      // 获取第一个元素
      int first = numbers.first();
      // 获取最后一个元素
      int last = numbers.last();

    // 删除元素(删):
    numbers.removeAt(1);
    qDebug() << "After removal of index 1:" << numbers; // 输出:[10, 20, 30]

    // 清除所有元素:
    numbers.clear();
    qDebug() << "After clear:" << numbers; // 输出:[]

    return 0;
}

总结:

QVector 非常适用于那些需要高效随机访问、不需要频繁插入删除操作的情况,尤其是当你预见到数据集大小可能变动但总体上保持相对稳定时。如果你的应用场景涉及到大量动态插入和删除操作,尤其是在容器中间位置,那么可能需要考虑使用 QList 或其他更适合的数据结构。

QLinkedList 详细介绍

QLinkedList 详细介绍

QLinkedList 是 Qt 框架中的一个模板类,它实现了双向链表数据结构,类似于 C++ STL 中的 std::list。QLinkedList 适用于频繁执行插入和删除操作,特别是在列表中间位置,而不依赖于高效的随机访问。

优点:

  1. 插入和删除效率:在任何位置插入和删除元素的时间复杂度通常为 O(1),这是因为链表结构不需要为了插入或删除元素而移动其他元素。
  2. 内存分配:每个元素都包含指向前后元素的指针,因此可以在非连续的内存区域存储数据,对于大规模数据操作时内存分配更为灵活。
  3. 迭代器支持:提供了前向迭代器和双向迭代器,方便在链表中进行遍历。

缺点:

  1. 随机访问速度:由于不是连续存储,QLinkedList 不支持通过索引快速访问元素,获取指定位置的元素需要从头或尾部开始遍历,时间复杂度为 O(n)。
  2. 内存消耗:除了存储数据本身,每个元素还需要额外的空间来存储指向前后节点的指针,这相对于连续存储的数据结构(如 QVector)增加了内存开销。

使用示例及增删改查功能演示:

#include <QLinkedList>
#include <QDebug>

int main() {
    // 创建一个空的 QLinkedList
    QLinkedList<int> numbers;

    // 添加元素(增):
    numbers.append(10);
    numbers.append(20);
    numbers.append(30);

    // 插入元素(增):
    numbers.insertBefore(numbers.begin(), 5); // 在开头插入5
    qDebug() << "After insertion:" << numbers; // 输出:[5, 10, 20, 30]

    // 修改元素(改):
    auto it = numbers.begin(); // 获取第一个元素的迭代器
    ++it; // 移动到第二个元素
    *it = 15; // 修改第二个元素的值
    qDebug() << "After modification:" << numbers; // 输出:[5, 15, 20, 30]

    // 查找并修改元素:
    for (auto it = numbers.begin(); it != numbers.end(); ++it) {
        if (*it == 20) {
            *it = 25;
            break;
        }
    }
    qDebug() << "After searching and modifying:" << numbers; // 输出:[5, 15, 25, 30]

    // 删除元素(删):
    it = numbers.begin(); // 获取第一个元素迭代器
    ++it; // 移动到要删除的元素
    numbers.erase(it); // 删除第二个元素
    qDebug() << "After removal:" << numbers; // 输出:[5, 25, 30]

    // 清空链表:
    numbers.clear();
    qDebug() << "After clear:" << numbers; // 输出:[]

    return 0;
}

总结:

QLinkedList 在插入和删除操作频繁,尤其当列表顺序经常改变的场合表现优秀。如果你的应用程序要求高效地在列表任意位置插入或删除数据,并且不十分依赖于随机访问的速度,QLinkedList 就是一个理想的选择。但对于需要快速随机访问或者对内存占用有严格限制的场景,则应考虑使用 QVector 等连续存储的数据结构。

QStack 详细介绍

QStack 详细介绍

QStack 是 Qt 框架提供的一个模板类,它实现了栈(Stack)这一数据结构,遵循后进先出(Last In, First Out, LIFO)原则。栈在编程中非常常见,主要用于那些需要"最后加入的元素最先取出"的逻辑场景。

优点:

  1. 操作简单 :QStack 提供了基本的堆栈操作,如 push()(入栈)、pop()(出栈)和 top()(查看栈顶元素),易于理解和使用。
  2. 高效:由于其简单的数据结构特性,入栈和出栈操作通常具有 O(1) 的时间复杂度。
  3. 兼容性:作为 Qt 容器类的一部分,QStack 可以很好地与其它 Qt 类型配合使用,并支持 Qt 的信号和槽机制。

缺点:

  1. 受限的功能:相较于其他的容器类如 QVector 和 QList,QStack 的功能较为有限,仅针对堆栈特性的操作进行了优化,不适合需要高效随机访问或在容器中部插入和删除元素的场景。
  2. 无内置查找方法:QStack 并未提供直接查找特定元素的方法,如果需要查找某个元素,需要手动遍历整个栈,这是栈数据结构本身的局限性。

使用示例及增删改查功能演示:

#include <QStack>
#include <QDebug>

int main() {
    // 创建一个空的 QStack
    QStack<int> stack;

    // 增加元素(入栈)
    stack.push(10);
    stack.push(20);
    stack.push(30);
    qDebug() << "After pushing elements:" << stack; // 输出可能是 "[30, 20, 10]",实际输出顺序取决于 Qt 库的实现

    // 查看栈顶元素(读取)
    int topElement = stack.top();
    qDebug() << "Top element is:" << topElement; // 输出:30

    // 删除并返回栈顶元素(出栈)
    int poppedElement = stack.pop();
    qDebug() << "Popped element is:" << poppedElement; // 输出:30
    qDebug() << "After popping an element:" << stack; // 输出可能是 "[20, 10]"

    // 注意:QStack 不支持在栈中直接修改元素,若需修改栈顶元素,需先 pop 再 push 修改后的值

    // 清空栈
    stack.clear();
    qDebug() << "After clearing the stack:" << stack; // 输出:""

    return 0;
}

// 注意:虽然 QStack 继承自 QVector 或 QList 的信息存在一些矛盾,实际上 QStack 的继承关系可能随 Qt 版本不同有所变化,
// 在某些版本中它继承自 QVector,而在另一些版本中可能继承自 QList,但无论如何,它都保留了栈的核心操作。

总结:QStack 主要用于简化堆栈操作,适用于需要快速存取和按先进后出顺序处理数据的场景。不过,由于其设计目的单一,对于需要复杂查询或排序需求的数据处理来说不太适用。另外,关于 QStack 继承自哪个基类,建议查阅最新的 Qt 文档以获得准确信息。

QQueue 详细介绍

QQueue 详细介绍

QQueue 是 Qt 框架中的一个模板类,它实现了队列(Queue)数据结构,遵循先进先出(First In, First Out, FIFO)的原则。队列是一个线性表,新添加的元素会被放在队列的末尾(称为enqueue),而从队列中移除或读取元素则会从队列的前端开始(称为dequeue)。

优点:

  1. 队列操作 :QQueue 提供了便捷的方法进行队列操作,比如 enqueue()(入队)和 dequeue()(出队)以及 head()(查看队首元素)等。
  2. 高效性:QQueue 继承自 QList,这意味着它可以充分利用 QList 的内存管理和性能优势,尤其是在队列头部和尾部插入或删除元素时,QList 的内存分配策略保证了较高的效率。
  3. 兼容性 :由于继承自 QList,QQueue 可以使用 QList 的大部分成员函数,例如 size()(获取队列长度)、clear()(清空队列)等,也可以使用迭代器进行遍历。

缺点:

  1. 线程安全性:QQueue 自身并非线程安全的,所以在多线程环境下需要开发者自己负责同步,通常可以通过互斥锁(QMutex)或其他并发控制机制来保证线程安全。
  2. 插入和删除中间元素:尽管继承自 QList,但由于队列操作的性质,不应该在队列中间进行插入和删除操作,否则将失去队列结构的意义,且可能导致较低的性能。

使用示例及增删改查功能演示:

#include <QQueue>
#include <QDebug>

int main() {
    // 创建一个空的 QQueue
    QQueue<int> numbersQueue;

    // 增加元素(入队)
    numbersQueue.enqueue(10);
    numbersQueue.enqueue(20);
    numbersQueue.enqueue(30);
    qDebug() << "After enqueueing elements:" << numbersQueue;

    // 查看队首元素(读取,但不移除)
    int frontElement = numbersQueue.head();
    qDebug() << "Front element is:" << frontElement;

    // 删除并返回队首元素(出队)
    int dequeuedElement = numbersQueue.dequeue();
    qDebug() << "Dequeued element is:" << dequeuedElement;
    qDebug() << "After dequeueing an element:" << numbersQueue;

    // 修改队列中的元素(由于队列只允许在队尾插入,此处假设我们先出队再重新入队修改后的值)
    numbersQueue.dequeue();
    numbersQueue.enqueue(frontElement + 1); // 修改原队首元素的值
    qDebug() << "After modifying an element by re-enqueuing:" << numbersQueue;

    // 清空队列
    numbersQueue.clear();
    qDebug() << "After clearing the queue:" << numbersQueue;

    return 0;
}

注意:QQueue 一般不提倡直接修改队列中的元素,因为它设计的目的在于线性、顺序的数据处理。如确实需要修改队列中的某元素,一般需要先出队再入队修改后的值。

总结:QQueue 适用于那些需要按顺序处理任务、消息传递或者其他先进先出逻辑的场景。它在单线程环境中表现良好,但在多线程环境时需要谨慎处理并发访问问题。由于其基于QList的设计,对尾部的增删操作非常高效,而对于非队首元素的操作则应避免。

QMap 详细介绍

QMap 详细介绍

QMap 是 Qt 框架中的一种关联容器模板类,它实现了关联数组或字典的功能,允许通过键(Key)来存储和检索对应的值(Value)。QMap 中的键值对是按照键的升序排列的,这是它与标准库中 std::map 的主要区别之一。

优点:

  1. 排序存储 :QMap 中的元素是有序的,根据键的自然排序规则(即键类型需要支持比较运算符 <),这使得可以通过键的顺序遍历整个映射。
  2. 查找性能:QMap 使用了红黑树(Red-Black Tree)数据结构,插入、删除和查找操作平均时间复杂度为 O(log n),其中 n 是映射中的元素数量。
  3. 内存管理:Qt 内存管理机制确保了在插入、删除过程中对内存的有效利用,减少不必要的内存复制和移动。
  4. 丰富的接口:QMap 提供了一系列便利的方法,包括但不限于插入、删除、查找、迭代遍历等操作。

缺点:

  1. 内存开销:由于红黑树需要额外的指针来维护平衡结构,QMap 相比于基于哈希表实现的 QHash,内存占用可能稍高。
  2. 键值类型的限制 :键类型必须支持比较运算符 <,这在自定义类型作为键时需要用户自行定义。
  3. 随机插入删除的效率:虽然查找速度快,但如果频繁在中间位置插入或删除元素,由于树结构调整的需要,可能不如无序的哈希表高效。

使用示例及增删改查功能演示:

#include <QMap>
#include <QDebug>

int main() {
    // 创建一个空的 QMap
    QMap<QString, int> studentGrades;

    // 插入元素(增)
    studentGrades.insert("Alice", 85);
    studentGrades.insert("Bob", 90);
    studentGrades.insert("Charlie", 88);
    qDebug() << "After inserting:" << studentGrades; // 输出键值对的集合

    // 修改元素(改)
    if (studentGrades.contains("Alice")) {
        studentGrades["Alice"] = 92; // 直接通过键修改值
    }
    qDebug() << "After modification:" << studentGrades;

    // 查找元素
    bool hasGrade = studentGrades.contains("Alice"); // 检查是否存在键
    int aliceGrade = studentGrades.value("Alice", -1); // 获取值,如果没有该键则返回默认值
    qDebug() << "Does 'Alice' have a grade? " << hasGrade;
    qDebug() << "Grade of Alice: " << aliceGrade;

    // 删除元素(删)
    studentGrades.remove("Bob");
    qDebug() << "After removal of 'Bob':" << studentGrades;

    // 迭代遍历
    foreach (const QString &name, studentGrades.keys()) {
        int grade = studentGrades.value(name);
        qDebug() << name << "has grade:" << grade;
    }

    return 0;
}

总结:

QMap 是一种适用于有序键值对存储的理想选择,特别是当应用程序需要按键的自然顺序遍历时。然而,如果对查找效率有极高要求且不在乎元素的顺序,或者需要频繁插入和删除元素,QHash 可能是更好的选择。QMap 能够有效处理各种复杂的键值存储需求,并且因其有序性而增强了代码的可读性和易用性。

QHash 详细介绍

QHash 是 Qt 框架中的一个容器类,用于存储键值对。

优点:

  • 快速的键值查找:基于哈希表实现,提供了高效的查找操作。
  • 自动内存管理:不需要手动分配和释放内存。
  • 简单易用:提供了方便的接口进行插入、查找、删除等操作。

缺点:

  • 不保证元素的顺序:元素的存储顺序是不确定的。

使用示例:

#include <QHash>
#include <QDebug>

int main() {
    // 创建 QHash 对象
    QHash<QString, int> hash;

    // 插入键值对
    hash["name"] = 10;
    hash["age"] = 20;
    hash["gender"] = 30;

    // 查找键对应的值
    int value = hash["name"];
    qDebug() << "Value for 'name' : " << value;

    // 删除键值对
    hash.remove("age");

    // 修改键对应的值
    hash["gender"] = 40;

    // 检查键是否存在
    if (hash.contains("name")) {
        qDebug() << "Key 'name' exists.";
    } else {
        qDebug() << "Key 'name' does not exist.";
    }

    return 0;
}

在上述示例中,演示了 QHash 的增删改查功能:

  • 增:使用 hash["key"] = value; 插入键值对。
  • 删:使用 hash.remove("key"); 删除键值对。
  • 改:直接重新赋值修改键对应的值。
  • 查:使用 hash.contains("key"); 检查键是否存在,使用 hash["key"]; 获取键对应的值。

QSet 详细介绍

QSet 是 Qt 框架中的一个集合类,用于存储唯一元素。

优点:

  • 自动去除重复元素,保证元素的唯一性。
  • 快速的查找操作。

缺点:

  • 不保证元素的顺序。

使用示例:

#include <QSet>
#include <QDebug>

int main() {
    // 创建 QSet 对象
    QSet<int> set;

    // 增加元素
    set << 1 << 2 << 3 << 4 << 5;

    // 检查元素是否存在
    if (set.contains(3)) {
        qDebug() << "Element 3 exists in the set.";
    } else {
        qDebug() << "Element 3 does not exist in the set.";
    }

    // 删除元素
    set.remove(2);

    // 修改(实际上是添加或删除)元素
    set.insert(6);

    // 遍历元素
    for (int element : set) {
        qDebug() << element;
    }

    return 0;
}

在上述示例中,演示了 QSet 的增删改查功能:

  • 增:使用 << 操作符或 insert() 函数添加元素。
  • 删:使用 remove() 函数删除元素。
  • 改:通过添加或删除元素来实现修改。
  • 查:使用 contains() 函数检查元素是否存在,使用遍历方式查看所有元素。

QStringList 详细介绍

QStringList 是 Qt 框架中的一个字符串列表类,用于存储和操作一系列字符串。

优点:

  1. 提供了方便的字符串列表操作接口。
  2. 自动管理内存。
  3. 支持各种常见的列表操作。

缺点:

  1. 不适合存储大量数据,性能可能会受到影响。

使用示例:

#include <QStringList>
#include <QDebug>

int main() {
    QStringList list;

    // 增加元素
    list << "apple" << "banana" << "orange";

    // 查询元素是否存在
    if (list.contains("apple")) {
        qDebug() << "Element 'apple' exists in the list.";
    } else {
        qDebug() << "Element 'apple' does not exist in the list.";
    }

    // 删除元素
    list.removeAll("banana");

    // 修改元素
    list[0] = "new_apple";

    // 遍历元素
    for (const QString &str : list) {
        qDebug() << str;
    }

    return 0;
}

在上述示例中,演示了 QStringList 的增删改查功能:

  1. 增:使用 << 操作符添加元素。
  2. 删:使用 removeAll() 函数删除元素。
  3. 改:通过索引访问并修改元素。
  4. 查:使用 contains() 函数检查元素是否存在,使用遍历方式查看所有元素。

QByteArray 详细介绍

在 Qt 中,QByteArray类被广泛用于处理二进制数据、图像、音频、视频等多媒体数据,也可以用于网络传输和文件操作等场景。其主要特点如下:

  • 高效性:QByteArray的内部实现使用了指针和引用计数技术,可以高效地存储和访问大量数据。
  • 功能强大:QByteArray提供了丰富的字节数组操作函数,例如appendinsertreplaceremove等,可以灵活地操作字节数组。
  • 支持多种编码方式:QByteArray支持多种编码方式,例如 ASCII、UTF-8、UTF-16 等,可以方便地处理不同编码的数据。

下面给出一个简单例子,用于入门QByteArray

#include <QByteArray>
#include <QDebug>

int main() {
    // 定义一个空 QByteArray 对象
    QByteArray qByteArray("");

    // 在尾部添加字符串
    qByteArray.append("daniel");

    // 返回数据指针
    qDebug() << "qByteArray = " << qByteArray.data() << "\n";

    // 返回大小,不保护末尾的 '\n'
    qDebug() << "The size of qByteArray is " << qByteArray.size() << "\n";

    // 查询重复次数
    qDebug() << "The number of occurrences of 'a' is " << qByteArray.count('a') << "\n";

    // 更改填充值
    qByteArray.fill('a');

    // 返回数据指针
    qDebug() << "qByteArray = " << qByteArray.data() << "\n";

    return 0;
}

以上容器类都是模板类,可以根据需要存储任何兼容类型的对象,并且它们在设计上力求简化接口、提高跨平台兼容性和线程安全性。相较于C++标准模板库(STL),Qt的容器类通常被认为更适合于GUI开发环境,尤其是考虑到多线程同步问题时。

相关推荐
Ni-Guvara1 分钟前
函数对象笔记
c++·算法
似霰5 分钟前
安卓智能指针sp、wp、RefBase浅析
android·c++·binder
芊寻(嵌入式)16 分钟前
C转C++学习笔记--基础知识摘录总结
开发语言·c++·笔记·学习
獨枭17 分钟前
C++ 项目中使用 .dll 和 .def 文件的操作指南
c++
霁月风20 分钟前
设计模式——观察者模式
c++·观察者模式·设计模式
橘色的喵21 分钟前
C++编程:避免因编译优化引发的多线程死锁问题
c++·多线程·memory·死锁·内存屏障·内存栅栏·memory barrier
一颗松鼠24 分钟前
JavaScript 闭包是什么?简单到看完就理解!
开发语言·前端·javascript·ecmascript
有梦想的咸鱼_26 分钟前
go实现并发安全hashtable 拉链法
开发语言·golang·哈希算法
海阔天空_201332 分钟前
Python pyautogui库:自动化操作的强大工具
运维·开发语言·python·青少年编程·自动化
天下皆白_唯我独黑39 分钟前
php 使用qrcode制作二维码图片
开发语言·php