深入学习 Arduino LinkedList库(一个变长的集合类数组)
QQ技术互动交流群:ESP8266&32 物联网开发 群号622368884,不喜勿喷
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库(一个变长的集合类数组)的更多相关文章
- 在C#中如何定义一个变长的结构数组?如果定义好了,如何获得当前数组的长度?
用ArrayList,他就相当于动态数组,用add方法添加元素,remove删除元素,count计算长度
- 算法题 -- 输入一个Long数组,按要求输出一个等长的Long数组
/** * 输入一个Long数组,按要求输出一个等长的Long数组 * 输出数组的元素值等于,输入数组除相同下标外其他元素的积 * 如:输入[1, 2, 3, 4], 输出[24, 12, 8, 6] ...
- C#如何定义一个变长的一维和二维数组
1.假设将要定义数组的长度为程序执行过程中计算出来的MAX List<int> Arc = new List<int>(); ; i < MAX; i++) { Arc. ...
- C++17尝鲜:变长 using 声明
using 声明 先来看 using 声明在类中的应用: 代码1 #include <iostream> using namespace std; struct A { void f(in ...
- C++中的变长参数
新参与的项目中,为了使用共享内存和自定义内存池,我们自己定义了MemNew函数,且在函数内部对于非pod类型自动执行构造函数.在需要的地方调用自定义的MemNew函数.这样就带来一个问题,使用stl的 ...
- SQL Server如何在变长列上存储索引
这篇文章我想谈下SQL Server如何在变长列上存储索引.首先我们创建一个包含变长列的表,在上面定义主键,即在上面定义了聚集索引,然后往里面插入80000条记录: -- Create a new t ...
- C99新特性:变长数组(VLA)
C99标准引入了变长数组,它允许使用变量定义数组各维.例如您可以使用下面的声明: ; ; double sales[rows][cols]; // 一个变长数组(VLA) 变长数组有一些限制,它必须是 ...
- [改善Java代码]若有必要,使用变长数组
Java中的数组是定长的,一旦经过初始化声明就不可改变长度,这在实际使用的时候非常不方便.比如要对一个班级的学生信息进行统计,因为我们不知道班级会有多少个学生(随时可能有退学,入学,转学),所以需要一 ...
- java 变长參数使用原则
1.java变长參数用...表示,如Print(String... args){ ... }; 2.假设一个调用既匹配一个固定參数方法.又匹配一个变长參数方法,则优先匹配固定參数的方法 3.假设一个 ...
随机推荐
- PHP Laravel 中使用简单的方法跟踪用户是否在线
今天,我的任务是,在 Laravel 应用程序用户个人资料页面上,用户名旁边添加一个绿点,表示他们是否在线.我首先想到的是,我们将需要启动一个 node.js 服务器并跟踪每个用户的活动套接字连接.然 ...
- 微信web协议,群成员唯一uin,获取群成员唯一标识
群成员唯一标识获取接口 全网最新,支持调试测试.觉得OK再付款! 800元出售源码 不讲价 联系QQ:2052404477
- CGI、FastCGI、CLI、Apache、ISAPI之PHP运行环境对比
1.运行模式 关于PHP目前比较常见的五大运行模式: 1)CGI(通用网关接口 / Common Gateway Interface) 2)FastCGI(常驻型CGI / Long-Live CGI ...
- 06_K-近邻算法
k-近邻算法 算法介绍 定义: 如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一类别,则该样本也属于这个类别. 计算距离公式:欧式距离 (两点之间距离) 需要做标准化 ...
- 力扣(LeetCode)从不订购的客户-数据库题 个人题解
SQL架构 某网站包含两个表,Customers 表和 Orders 表.编写一个 SQL 查询,找出所有从不订购任何东西的客户. Customers 表: +----+-------+ | Id | ...
- 使用C#+FFmpeg+DirectX+dxva2硬件解码播放h264流
本文门槛较高,因此行文看起来会乱一些,如果你看到某处能会心一笑请马上联系我开始摆龙门阵 如果你跟随这篇文章实现了播放器,那你会得到一个高效率,低cpu占用(单路720p视频解码播放占用1%左右cpu) ...
- three.js使用卷积法实现物体描边效果
法线延展法 网上使用法线延展法实现物体描边效果的文章比较多,这里不再描述. 但是这种方法有个缺点:当两个面的法线夹角差别较大时,两个面的描边无法完美连接.如下图所示: 卷积法 这里使用另一种方法卷积法 ...
- Mac 下安装并配置 Tomcat
1,下载 点击 官网 ,进入下载页面, 2,安装 解压出来,即安装完成. 移动解压后的文件,换个文件目录(方便集中管理),将它改个名字(毕竟名字太长了). 我将其改名为 tomcat9 ,移入资源库目 ...
- linux后台运行程序--nobup
用途:不挂断地运行命令. 语法:nohup Command [ Arg - ] [ & ] 描述:nohup 命令运行由 Command 参数和任何相关的 Arg 参数指定的命令,忽略所有挂断 ...
- 护网杯2019 mergeheap --pwn
护网 又是签到 一天 这道题一开始 不懂得如何泄露 libc 信息,就蒙了 后来群里师傅也是刚刚好 做出 到这里 我就接着做了 . 先看下保护,发现 全开了 然后 就看下流程 大概 就是添加 c ...