QQ技术互动交流群:ESP8266&32 物联网开发 群号622368884,不喜勿喷

单片机菜鸟博哥CSDN

1.前言

博主是做Android App开发出身的,所以对Java这门语言算是有点了解。而在Java中,我经常使用到List这个集合类(所以集合,博主理解为一系列同类对象集中在一起的入口)。那么,在Arduino中是否也有这样类似于Java 的List用法呢?这就是本篇内容的重点 —— Arduino LinkedList

2.LinkedList

2.1 LinkedList源码地址

  • 直接点击 这里 下载代码

    2.2 LinkedList是什么

  • 一系列 同类对象的集合(这点属性和数组一样)
  • 可以动态添加或者删除对象,长度不固定(这点属性就是和数组非常大的区别,数组都是分配固定大小的内存空间,当你考虑动态长度的时候可以考虑这个库
  • 支持对象为 int, float, objects, Lists

    2.3 安装LinkedList

  • 可以通过Arduino IDE库管理器安装LinkedList,搜索“LinkedList”,然后点击下载(适合新手小白,直接上手使用)
  • 也可以直接通过github下载源码,放到你的Arduino libraries文件夹

2.4 LinkedList工作原理

  • 工作原理非常简单,就是构造链表,每个节点相连形成一条链,这样可以单独添加节点或者删除节点(请读者自行就查阅链表的数据结构)

  • LinkedList源码中定义的节点结构

template<class T> // 泛型
struct ListNode
{
    T data; // 每个节点对应的泛型数据
    ListNode<T> *next;// 每个节点的下一个节点,这样就形成了链表
};

请记住关键的一点,除了最后一个节点,其他节点都会包含下一个节点的地址。

2.5 如何引入 LinkedList库

非常简单,直接引入:

#include <LinkedList.h>

至于怎么样去彻底了解这个库的使用方法,请看博主下面的源码分析。

3.LinkedList源码解析

通过查阅WifiManager的源码,整体代码非常简单,提供出来的方法调用也不多。这里博主就一个个方法详细讲解,并且深入去分析代码。方法分为几类:

  • 初始化方法
  • 添加类方法
  • 删除类方法
  • 其他方法

博主建议:先了解有什么方法,然后在后面例子讲解中去感受方法的使用

3.1 初始化方法

简而言之,就是怎么创建构造函数。

3.1.1 LinkedList —— 初始化构造函数

构造函数有两个,一个不初始化集合中的对象,一个对集合中的对象进行默认初始化。

函数1说明

// 初始化所有的原始默认值,包括头尾节点,集合大小,上一个节点
template<typename T>
LinkedList<T>::LinkedList()
{
    root=NULL;// 头结点
    last=NULL;// 尾结点
    _size=0;// 集合大小

    lastNodeGot = root; // 上一个节点
    lastIndexGot = 0;  // 上一个节点对应的索引
    isCached = false;  //这个暂时没知道怎么用
}

函数2说明

// 初始化集合的数据 从0-(sizeIndex-1)赋值为 _t
template<typename T>
LinkedList<T>::LinkedList(int sizeIndex, T _t){
    for (int i = 0; i < sizeIndex; i++){
        add(_t);
    }
}

个人习惯用函数1.

3.2 添加类方法

添加类方法,也就是往链表添加对象,长度+1。

3.2.1 add —— 添加对象到某个位置

针对添加位置的不同,又区分为两个方法。

函数1说明

template<typename T>
bool LinkedList<T>::add(T _t){

    // 创建一个新的节点
    ListNode<T> *tmp = new ListNode<T>();
    tmp->data = _t;
    tmp->next = NULL;
    // 判断是否已经有对象插入
    if(root){
        // Already have elements inserted
        last->next = tmp;
        last = tmp;
    }else{
        // 第一个对象
        root = tmp;
        last = tmp;
    }
    // 集合大小加1
    _size++;
    isCached = false;

    return true;
}

函数2说明

template<typename T>
bool LinkedList<T>::add(int index, T _t){
    // index超过size,直接添加到表尾
    if(index >= _size)
        return add(_t);
    // index=0 插入表头
    if(index == 0)
        return unshift(_t);
    // 创建一个新的节点
    ListNode<T> *tmp = new ListNode<T>(),
                 // 获取index-1这个节点,也就是插入点的位置
                 *_prev = getNode(index-1);
    // 断开链表 并且把新节点插入对应点 连接链表
    tmp->data = _t;
    tmp->next = _prev->next;
    _prev->next = tmp;

    _size++;
    isCached = false;

    return true;
}

这里用到了一个 获取节点 的方法 getNode

template<typename T>
ListNode<T>* LinkedList<T>::getNode(int index){

    int _pos = 0;
    ListNode<T>* current = root;

    // Check if the node trying to get is
    // immediatly AFTER the previous got one
    if(isCached && lastIndexGot <= index){
        _pos = lastIndexGot;
        current = lastNodeGot;
    }

    // 找到对应的索引位置
    while(_pos < index && current){
        current = current->next;

        _pos++;
    }

    // Check if the object index got is the same as the required
    if(_pos == index){
        // 找到对应的节点内容 返回节点
        isCached = true;
        lastIndexGot = index;
        lastNodeGot = current;

        return current;
    }

    return NULL;
}

3.2.2 unshift —— 添加对象到链表头部位置

函数说明

template<typename T>
bool LinkedList<T>::unshift(T _t){
    // 链表为空直接插入
    if(_size == 0)
        return add(_t);

    // 设置当前节点的下一个节点是原来的root节点,这样就插入到头部了
    ListNode<T> *tmp = new ListNode<T>();
    tmp->next = root;
    tmp->data = _t;
    root = tmp;

    _size++;
    isCached = false;

    return true;
}

3.3 删除类方法

删除类方法,也就是链表删除对象,长度-1。

3.3.1 remove —— 移除某个位置的对象,并且返回该对象

函数说明

template<typename T>
T LinkedList<T>::remove(int index){
    // 如果index超出了范围 直接返回一个空对象
    if (index < 0 || index >= _size)
    {
        return T();
    }
    // index=0 返回头部对象
    if(index == 0)
        return shift();
    // 返回尾部对象
    if (index == _size-1)
    {
        return pop();
    }

    // 取出对象后 还是重新构建链表
    ListNode<T> *tmp = getNode(index - 1);
    ListNode<T> *toDelete = tmp->next;
    T ret = toDelete->data;
    tmp->next = tmp->next->next;
    // 删除对象 释放内存
    delete(toDelete);
    _size--;
    isCached = false;
    return ret;
}

3.3.2 pop —— 移除链表尾部的对象

函数说明

template<typename T>
T LinkedList<T>::pop(){
    // 空集合 就返回空对象
    if(_size <= 0)
        return T();

    isCached = false;
    // 多个元素 就获取最后一个元素
    if(_size >= 2){
        ListNode<T> *tmp = getNode(_size - 2);
        T ret = tmp->next->data;
        // 释放内存
        delete(tmp->next);
        tmp->next = NULL;
        last = tmp;
        _size--;
        return ret;
    }else{
        //只有一个头元素
        // Only one element left on the list
        T ret = root->data;
        delete(root);
        root = NULL;
        last = NULL;
        _size = 0;
        return ret;
    }
}

3.3.3 shift —— 移除链表头部的对象

函数说明

template<typename T>
T LinkedList<T>::shift(){
    // 空集合 返回空对象
    if(_size <= 0)
        return T();
    // 删除头对象 并且把下一个对象设置为头对象
    if(_size > 1){
        ListNode<T> *_next = root->next;
        T ret = root->data;
        delete(root);
        root = _next;
        _size --;
        isCached = false;

        return ret;
    }else{
        // Only one left, then pop()
        return pop();
    }

}

3.3.4 clear —— 清除整个链表

函数说明

template<typename T>
void LinkedList<T>::clear(){
    while(size() > 0)
        shift();
}

3.4 其他方法

3.4.1 set —— 设置某个位置的对象

函数说明

template<typename T>
bool LinkedList<T>::set(int index, T _t){
    // 判断index是否在有效范围
    if(index < 0 || index >= _size)
        return false;

    getNode(index)->data = _t;
    return true;
}

3.4.2 get —— 获取某个位置的对象

函数说明

template<typename T>
T LinkedList<T>::get(int index){
    ListNode<T> *tmp = getNode(index);

    return (tmp ? tmp->data : T());
}

3.4.3 sort —— 对集合进行排序

函数说明 此方法还没有支持

template<typename T>
void LinkedList<T>::sort(int (*cmp)(T &, T &)){
    if(_size < 2) return; // trivial case;

    for(;;) {   

        ListNode<T> **joinPoint = &root;

        while(*joinPoint) {
            ListNode<T> *a = *joinPoint;
            ListNode<T> *a_end = findEndOfSortedString(a, cmp);

            if(!a_end->next ) {
                if(joinPoint == &root) {
                    last = a_end;
                    isCached = false;
                    return;
                }
                else {
                    break;
                }
            }

            ListNode<T> *b = a_end->next;
            ListNode<T> *b_end = findEndOfSortedString(b, cmp);

            ListNode<T> *tail = b_end->next;

            a_end->next = NULL;
            b_end->next = NULL;

            while(a && b) {
                if(cmp(a->data, b->data) <= 0) {
                    *joinPoint = a;
                    joinPoint = &a->next;
                    a = a->next;
                }
                else {
                    *joinPoint = b;
                    joinPoint = &b->next;
                    b = b->next;
                }
            }

            if(a) {
                *joinPoint = a;
                while(a->next) a = a->next;
                a->next = tail;
                joinPoint = &a->next;
            }
            else {
                *joinPoint = b;
                while(b->next) b = b->next;
                b->next = tail;
                joinPoint = &b->next;
            }
        }
    }
}

4.官方实例操作

4.1 ClassList —— 以动物为例

实例源码

// 以动物为例 创建 cat dog emu 一个个加入list
#include <LinkedList.h>

// Let's define a new class
class Animal {
    public:
        char *name;
        bool isMammal;
};

char  catname[]="kitty";
char  dogname[]="doggie";
char  emuname[]="emu";

LinkedList<Animal*> myAnimalList = LinkedList<Animal*>();

void setup()
{

    Serial.begin(9600);
    Serial.println("Hello!" );

    // Create a Cat
    Animal *cat = new Animal();
    cat->name = catname;
    cat->isMammal = true;

    // Create a dog
    Animal *dog = new Animal();
    dog->name = dogname;
    dog->isMammal = true;

    // Create a emu
    Animal *emu = new Animal();
    emu->name = emuname;
    emu->isMammal = false; // just an example; no offense to pig lovers

    // Add animals to list
    myAnimalList.add(cat);
    myAnimalList.add(emu);
    myAnimalList.add(dog);
}

void loop() {

    Serial.print("There are ");
    Serial.print(myAnimalList.size());
    Serial.print(" animals in the list. The mammals are: ");

    int current = 0;
    Animal *animal;
    for(int i = 0; i < myAnimalList.size(); i++){

        // Get animal from list
        animal = myAnimalList.get(i);

        // If its a mammal, then print it's name
        if(animal->isMammal){

            // Avoid printing spacer on the first element
            if(current++)
                Serial.print(", ");

            // Print animal name
            Serial.print(animal->name);
        }
    }
    Serial.println(".");

    while (true); // nothing else to do, loop forever
}

4.2 SimpleIntegerList——简单int类型集合

实例源码

// int为类型的list集合
#include <LinkedList.h>

LinkedList<int> myList = LinkedList<int>();

void setup()
{

    Serial.begin(9600);
    Serial.println("Hello!");

    // Add some stuff to the list
    int k = -240,
        l = 123,
        m = -2,
        n = 222;
    myList.add(n);
    myList.add(0);
    myList.add(l);
    myList.add(17);
    myList.add(k);
    myList.add(m);
}

void loop() {

    int listSize = myList.size();

    Serial.print("There are ");
    Serial.print(listSize);
    Serial.print(" integers in the list. The negative ones are: ");

    // Print Negative numbers
    for (int h = 0; h < listSize; h++) {

        // Get value from list
        int val = myList.get(h);

        // If the value is negative, print it
        if (val < 0) {
            Serial.print(" ");
            Serial.print(val);
        }
    }

    while (true); // nothing else to do, loop forever
}

5.总结

这里只是简单对LinkedList做了介绍,喜欢就麻烦给博主加以点赞关注。我个人觉得可以应用于此类场景:

  • 特别是上传温度之类的需求,不断上传温度,可以构造list集合,然后当做一个缓存来用,不断链表尾部加入数据,然后头部不断取出数据上报,

深入学习 Arduino LinkedList库(一个变长的集合类数组)的更多相关文章

  1. 在C#中如何定义一个变长的结构数组?如果定义好了,如何获得当前数组的长度?

    用ArrayList,他就相当于动态数组,用add方法添加元素,remove删除元素,count计算长度

  2. 算法题 -- 输入一个Long数组,按要求输出一个等长的Long数组

    /** * 输入一个Long数组,按要求输出一个等长的Long数组 * 输出数组的元素值等于,输入数组除相同下标外其他元素的积 * 如:输入[1, 2, 3, 4], 输出[24, 12, 8, 6] ...

  3. C#如何定义一个变长的一维和二维数组

    1.假设将要定义数组的长度为程序执行过程中计算出来的MAX List<int> Arc = new List<int>(); ; i < MAX; i++) { Arc. ...

  4. C++17尝鲜:变长 using 声明

    using 声明 先来看 using 声明在类中的应用: 代码1 #include <iostream> using namespace std; struct A { void f(in ...

  5. C++中的变长参数

    新参与的项目中,为了使用共享内存和自定义内存池,我们自己定义了MemNew函数,且在函数内部对于非pod类型自动执行构造函数.在需要的地方调用自定义的MemNew函数.这样就带来一个问题,使用stl的 ...

  6. SQL Server如何在变长列上存储索引

    这篇文章我想谈下SQL Server如何在变长列上存储索引.首先我们创建一个包含变长列的表,在上面定义主键,即在上面定义了聚集索引,然后往里面插入80000条记录: -- Create a new t ...

  7. C99新特性:变长数组(VLA)

    C99标准引入了变长数组,它允许使用变量定义数组各维.例如您可以使用下面的声明: ; ; double sales[rows][cols]; // 一个变长数组(VLA) 变长数组有一些限制,它必须是 ...

  8. [改善Java代码]若有必要,使用变长数组

    Java中的数组是定长的,一旦经过初始化声明就不可改变长度,这在实际使用的时候非常不方便.比如要对一个班级的学生信息进行统计,因为我们不知道班级会有多少个学生(随时可能有退学,入学,转学),所以需要一 ...

  9. java 变长參数使用原则

    1.java变长參数用...表示,如Print(String... args){  ... }; 2.假设一个调用既匹配一个固定參数方法.又匹配一个变长參数方法,则优先匹配固定參数的方法 3.假设一个 ...

随机推荐

  1. PHP Laravel 中使用简单的方法跟踪用户是否在线

    今天,我的任务是,在 Laravel 应用程序用户个人资料页面上,用户名旁边添加一个绿点,表示他们是否在线.我首先想到的是,我们将需要启动一个 node.js 服务器并跟踪每个用户的活动套接字连接.然 ...

  2. 微信web协议,群成员唯一uin,获取群成员唯一标识

    群成员唯一标识获取接口 全网最新,支持调试测试.觉得OK再付款! 800元出售源码 不讲价 联系QQ:2052404477

  3. CGI、FastCGI、CLI、Apache、ISAPI之PHP运行环境对比

    1.运行模式 关于PHP目前比较常见的五大运行模式: 1)CGI(通用网关接口 / Common Gateway Interface) 2)FastCGI(常驻型CGI / Long-Live CGI ...

  4. 06_K-近邻算法

    k-近邻算法 算法介绍 定义: 如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一类别,则该样本也属于这个类别. 计算距离公式:欧式距离 (两点之间距离) 需要做标准化 ...

  5. 力扣(LeetCode)从不订购的客户-数据库题 个人题解

    SQL架构 某网站包含两个表,Customers 表和 Orders 表.编写一个 SQL 查询,找出所有从不订购任何东西的客户. Customers 表: +----+-------+ | Id | ...

  6. 使用C#+FFmpeg+DirectX+dxva2硬件解码播放h264流

    本文门槛较高,因此行文看起来会乱一些,如果你看到某处能会心一笑请马上联系我开始摆龙门阵 如果你跟随这篇文章实现了播放器,那你会得到一个高效率,低cpu占用(单路720p视频解码播放占用1%左右cpu) ...

  7. three.js使用卷积法实现物体描边效果

    法线延展法 网上使用法线延展法实现物体描边效果的文章比较多,这里不再描述. 但是这种方法有个缺点:当两个面的法线夹角差别较大时,两个面的描边无法完美连接.如下图所示: 卷积法 这里使用另一种方法卷积法 ...

  8. Mac 下安装并配置 Tomcat

    1,下载 点击 官网 ,进入下载页面, 2,安装 解压出来,即安装完成. 移动解压后的文件,换个文件目录(方便集中管理),将它改个名字(毕竟名字太长了). 我将其改名为 tomcat9 ,移入资源库目 ...

  9. linux后台运行程序--nobup

    用途:不挂断地运行命令. 语法:nohup Command [ Arg - ] [ & ] 描述:nohup 命令运行由 Command 参数和任何相关的 Arg 参数指定的命令,忽略所有挂断 ...

  10. 护网杯2019 mergeheap --pwn

    护网 又是签到 一天 这道题一开始 不懂得如何泄露 libc 信息,就蒙了  后来群里师傅也是刚刚好 做出 到这里 我就接着做了 . 先看下保护,发现  全开了 然后 就看下流程 大概 就是添加  c ...