深入学习 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读取Excel内的图片
今天接到了一个从Excel内读取图片的需求,在网上查找了一些资料,基本实现了自己的需求,不过由于查到的一些代码比较久远,不能直接移植到自己的项目里,需要稍加改动一下. 这里介绍一下分别使用phpspr ...
- suseoj The wheat of the prime minister
1202: 2018四川理工学院大学生ACM程序设计:The wheat of the prime minister 时间限制: 1 Sec 内存限制: 128 MB提交: 4 解决: 3[提交] ...
- 力扣(LeetCode)从不订购的客户-数据库题 个人题解
SQL架构 某网站包含两个表,Customers 表和 Orders 表.编写一个 SQL 查询,找出所有从不订购任何东西的客户. Customers 表: +----+-------+ | Id | ...
- Pashmak and Graph(dp + 贪心)
题目链接:http://codeforces.com/contest/459/problem/E 题意:给一个带权有向图, 找出其中最长上升路的长度. 题解:先按权值对所有边排序, 然后依次 u -& ...
- sql注入基本原理
SQL注入基本原理 WEB技术发展日新月异,但是徒手拼SQL的传统手艺还是受相当多的开发者亲睐.毕竟相比于再去学习一套复杂的ORM规则,手拼更说方便,直观.通常自己拼SQL的人,应该是有听说过SQL注 ...
- 你必须知道的容器日志 (2) 开源日志管理方案 ELK
本篇已加入<.NET Core on K8S学习实践系列文章索引>,可以点击查看更多容器化技术相关系列文章.上一篇<你必须知道的容器日志(1)>中介绍了Docker自带的log ...
- ubuntu 16.04上源码编译dlib教程 | compile dlib on ubuntu 16.04
本文首发于个人博客https://kezunlin.me/post/c6ead512/,欢迎阅读! compile dlib on ubuntu 16.04 Series Part 1: compil ...
- ArcGIS 发布Feature服务
运行环境: Win10 ArcGIS10.4 具体操作: 1.打开ArcMap,加载sde中导入的文件,也可以加载shp数据源指向sde中文件 2.保存成mxd,然后点share as-Service ...
- 性能测试——记XX银行保全项目性能问题分析优化
记XX银行保全项目性能问题分析优化 数据库问题也许是大部分性能问题的关注点,但是JAVA应用与数据库交互的关节,JDBC 就像是我们人体的上半身跟下半身的腰椎,支持上半身,协调下半身运动的重要支撑点. ...
- web网站常用功能测试点总结
目录 一.输入框 二.搜索功能 三.添加.修改功能 四.删除功能 五.注册.登录模块 六.上传图片测试 七.查询结果列表 八.返回键检查 九.回车键检查 十.刷新键检查 十一.直接URL链接检查 十二 ...
