Java ArrayList 与顺序表:在编程海洋中把握数据结构的关键之锚

在这里插入图片描述

我的个人主页
我的专栏Java-数据结构,希望能帮助到大家!!!点赞❤ 收藏❤

前言:在 Java编程的广袤世界里,数据结构犹如精巧的建筑蓝图,决定着程序在数据处理与存储时的效率、灵活性以及可扩展性。其中,ArrayList和顺序表作为线性数据结构的典型代表,犹如两颗璀璨的明星,在众多数据处理场景中熠熠生辉。
顺序表,以其简洁而直观的连续内存存储方式,为数据的快速随机访问提供了可能。它犹如一位严谨的管家,将数据元素有条不紊地排列在连续的内存空间中,使得我们能够通过精确的索引迅速定位到所需的数据,就像在一本精心编排索引的书籍中快速找到特定章节一样高效。这种特性在一些对数据访问速度有严格要求且数据量相对固定或可预知的场景中,展现出了无与伦比的优势。
而 ArrayList,则是 Java 集合框架中备受瞩目的一员。它基于数组实现,却又巧妙地融入了动态扩容机制,如同一位富有弹性的艺术家,能够根据创作需求灵活调整画布的大小。这种动态性使得
ArrayList在应对数据量不确定、频繁增删元素的复杂情况时游刃有余。它为开发者提供了丰富便捷的方法库,极大地简化了数据操作的流程,仿佛一把瑞士军刀,在各种数据处理任务中都能发挥出色的作用。
在实际的编程旅程中,深入理解 ArrayList 和顺序表的特性、性能表现以及适用场景,就如同掌握了两门精湛的技艺。无论是构建高效的数据库管理系统、开发复杂的算法,还是打造用户友好的图形界面应用,都离不开对这两种数据结构的精准运用。本博客将深入剖析
Java ArrayList 和顺序表,从原理到实现,从性能对比到应用场景分析,为你揭开它们神秘的面纱,引领你在 Java编程的数据结构之路上稳步前行,帮助你在面对各种数据处理挑战时做出明智而高效的抉择。

一:线性表

二:顺序表

三:ArrayList的简介

四:ArrayList使用

五:ArrayList的扩容机制

一:线性表

线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列…
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。

Java 编程领域,线性表是一种极为基础且关键的数据结构,它犹如大厦的基石,支撑起众多复杂算法与程序功能的实现。线性表,从直观上理解,是由一系列具有相同类型的数据元素按照特定的顺序依次排列而成的集合,其元素之间存在着一对一的线性关系。
1.1线性表的基本特性:
(一)有限性
线性表中的元素数量是有限的。无论是空表(不包含任何元素),还是包含大量元素的表,其元素个数总能确定。例如,一个班级的学生名单,无论班级规模大小,学生人数总归是可以明确统计的。
(二)有序性
元素在表中的排列具有确定的顺序。每个元素都有其特定的位置,除了第一个元素无前驱,最后一个元素无后继外,其他元素都有且仅有一个直接前驱和一个直接后继。就像电影院里的座位号,每个座位都有其固定的前后顺序。
(三)同一性
表中的所有元素都属于同一数据类型。这一特性使得对线性表的操作能够具有一致性和规范性。例如,一个存储整数的线性表,其中所有元素都为整型,这样在进行数学运算或数据比较等操作时就可以按照统一的整型规则进行。
1.2线性表在 Java 中的常见实现方式
存储结构
顺序表利用数组来存储线性表中的元素。在 Java 中,可以声明一个数组来容纳这些元素,例如 Object[] data(或指定具体类型如 int[] data 等)。数组在内存中是连续分配空间的,这使得顺序表在随机访问元素时具有极高的效率。通过元素的索引,可以直接计算出其在内存中的存储地址,从而快速获取到该元素,时间复杂度为 O(1)。
操作实现
插入操作:当向顺序表中插入一个元素时,如果插入位置不是在表尾,需要将插入位置之后的所有元素依次向后移动一位,为新元素腾出空间,然后再将新元素放入指定位置。这种移动操作在元素数量较多时可能会耗费较多时间,平均时间复杂度为O(n) ,其中 为线性表的长度。例如,在一个长度为 100 的顺序表中,如果要在第 50 个位置插入一个元素,就需要将第 50 个到第 100 个元素向后移动一位。
删除操作:类似地,删除操作如果不是删除表尾元素,需要将删除位置之后的元素依次向前移动一位,以填补删除元素后留下的空位。其平均时间复杂度也为O(n) 。比如删除一个长度为 80 的顺序表中的第 30 个元素,就需要将第 31 个到第 80 个元素向前移动一位。
查询操作:由于可以直接通过索引访问数组元素,所以查询指定位置元素的时间复杂度为O(1) 。而查询某个特定值的元素时,可能需要遍历整个顺序表,在最坏情况下时间复杂度为O(n) 。例如,在一个存储学生成绩的顺序表中,如果要查找成绩为 90 分的学生,可能需要逐个比较所有学生的成绩。
二:顺序表
在这里插入图片描述
在这里插入图片描述

顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成 数据的增删查改。

1.1定义一个类来表示顺序表:

class ArrayList<E> {// 存储数据的数组private E[] data;// 记录当前顺序表中元素的个数private int size;// 构造函数,初始化数组容量public ArrayList(int capacity) {data = (E[]) new Object[capacity];size = 0;}
}

这里的 data 数组就是用来存放顺序表元素的核心容器,而 size 则用于追踪实际存储的元素数量。
2.1 接口的实现

public class SeqList {
private int[] array;
private int size;
// 默认构造方法
SeqList(){ }
// 将顺序表的底层容量设置为initcapacity
SeqList(int initcapacity){ }
// 新增元素,默认在数组最后新增
public void add(int data) { }
// 在 pos 位置新增元素
public void add(int pos, int data) { }
// 判定是否包含某个元素
public boolean contains(int toFind) { return true; }
// 查找某个元素对应的位置
public int indexOf(int toFind) { return -1; }
// 获取 pos 位置的元素
public int get(int pos) { return -1; }
// 给 pos 位置的元素设为 value
public void set(int pos, int value) { }
//删除第一次出现的关键字key
public void remove(int toRemove) { }
// 获取顺序表长度
public int size() { return 0; }
// 清空顺序表
public void clear() { }
// 打印顺序表,注意:该方法并不是顺序表中的方法,为了方便看测试结果给出的
public void display() { }
}

插入操作
当向顺序表中插入一个元素时,情况会相对复杂一些。如果是在顺序表的末尾插入元素,操作较为简单,只需将元素放入 data[size] 的位置,并将 size 加 1 即可。但如果是在中间位置插入,例如在索引 k 处插入元素 e,则需要将索引 k 及之后的元素依次向后移动一位,为新元素腾出空间,然后再将新元素放入索引 k 处。

public void add(int index, E e) {if (index < 0 || index > size) {throw new IllegalArgumentException("Add failed. Illegal index.");}if (size == data.length) {resize(2 * data.length);}for (int i = size - 1; i >= index; i--) {data[i + 1] = data[i];}data[index] = e;size++;
}

删除操作
删除顺序表中的元素同样需要考虑多种情况。如果删除的是末尾元素,直接将 size 减 1 即可。若删除索引为 k 的元素,则需要将索引 k 之后的元素依次向前移动一位,覆盖掉要删除的元素。

public E remove(int index) {if (index < 0 || index >= size) {throw new IllegalArgumentException("Remove failed. Illegal index.");}E ret = data[index];for (int i = index + 1; i < size; i++) {data[i - 1] = data[i];}size--;data[size] = null;if (size == data.length / 4 && data.length / 2!= 0) {resize(data.length / 2);}return ret;
}

三:ArrayList的简介

ArrayList是 Java 集合框架中的一个重要类,它位于java.util包中。它实现了List接口,这意味着它是一个有序的集合,允许存储重复的元素。它的底层数据结构是一个动态大小的数组,这使得它可以根据需要自动调整大小。
与普通数组相比,ArrayList更加灵活。普通数组在创建时就确定了大小,之后很难改变其容量,而ArrayList可以方便地添加或删除元素,并且会自动处理数组容量的扩展和收缩。

在这里插入图片描述

说明:
1. ArrayList是以泛型⽅式实现的,使⽤时必须要先实例化
2. ArrayList实现了RandomAccess接⼝,表明ArrayList⽀持随机访问
3. ArrayList实现了Cloneable接⼝,表明ArrayList是可以clone的
4. ArrayList实现了Serializable接⼝,表明ArrayList是⽀持序列化的
5. 和Vector不同,ArrayList不是线程安全的,在单线程下可以使⽤,在多线程中可以选择Vector或者 CopyOnWriteArrayList
6. ArrayList底层是⼀段连续的空间,并且可以动态扩容,是⼀个动态类型的顺序表

创建ArrayList对象
要使用ArrayList,首先需要导入java.util.ArrayList类。

import java.util.ArrayList;
public class ArrayListExample {public static void main(String[] args) {ArrayList<String> list = new ArrayList<String>();}
}
创建了一个可以存储String类型元素的ArrayList。其中是泛型参数,用于指定列表中元素的类型。除了String,还可以是其他基本数据类型对应的包装类(如Integer、Double等)或自定义的类类型。

四:ArrayList使用
4.1 ArrayList的构造
在这里插入图片描述

public static void main(String[] args) {
// ArrayList创建,推荐写法
// 构造⼀个空的列表
List<Integer> list1 = new ArrayList<>();
// 构造⼀个具有10个容量的列表
List<Integer> list2 = new ArrayList<>(10);
list2.add(1);
list2.add(2);
list2.add(3);
// list2.add("hello"); // 编译失败,List<Integer>已经限定了,list2中只能存储整
形元素
// list3构造好之后,与list中的元素⼀致
ArrayList<Integer> list3 = new ArrayList<>(list2);
// 避免省略类型,否则:任意类型的元素都可以存放,使⽤时将是⼀场灾难
List list4 = new ArrayList();
list4.add("111");
list4.add(100);
}

4.2 ArrayList常⻅操作
在这里插入图片描述

4.2.1添加元素

add(E e)方法:用于在ArrayList末尾添加元素。
import java.util.ArrayList;
public class ArrayListAddExample {public static void main(String[] args) {ArrayList<String> fruits = new ArrayList<>();fruits.add("Apple");fruits.add("Banana");System.out.println(fruits); }
}

首先创建了一个ArrayList对象fruits,用于存储String类型的元素。然后通过add方法添加了两个水果名称,最后打印出ArrayList,输出结果为[Apple, Banana]。

add(int index, E e)方法:用于在指定索引index处插入元素e。
import java.util.ArrayList;
public class ArrayListAddAtIndexExample {public static void main(String[] args) {ArrayList<String> fruits = new ArrayList<>();fruits.add("Apple");fruits.add("Banana");fruits.add(1, "Cherry");System.out.println(fruits); }
}

这里在索引为1的位置插入了"Cherry",原来索引为1(即"Banana")及其后面的元素会向后移动一位。输出结果为[Apple, Cherry, Banana]。

4.2.2获取元素

get(int index)方法:用于获取ArrayList中指定索引index处的元素。
import java.util.ArrayList;
public class ArrayListGetExample {public static void main(String[] args) {ArrayList<String> fruits = new ArrayList<>();fruits.add("Apple");fruits.add("Banana");String secondFruit = fruits.get(1);System.out.println(secondFruit); }
}

通过get(1)获取了索引为1的元素,即"Banana",并将其存储在secondFruit变量中,最后打印输出。

4.2.3修改元素

直接通过索引赋值:可以像操作数组一样,通过指定索引来修改ArrayList中的元素。
import java.util.ArrayList;
public class ArrayListSetExample {public static void main(String[] args) {ArrayList<String> fruits = new ArrayList<>();fruits.add("Apple");fruits.add("Banana");fruits.set(1, "Orange");System.out.println(fruits); }
}

通过set(1, “Orange”)将索引为1的元素(原来为"Banana")修改为"Orange",输出结果为[Apple, Orange]。

4.2.4删除元素

remove(int index)方法:用于删除指定索引index处的元素。
import java.util.ArrayList;
public class ArrayListRemoveByIndexExample {public static void main(String[] args) {ArrayList<String> fruits = new ArrayList<>();fruits.add("Apple");fruits.add("Banana");fruits.remove(0);System.out.println(fruits); }
}

通过remove(0)删除了索引为0的元素(即"Apple"),输出结果为[Banana]。

remove(Object o)方法:用于删除指定的对象o。
import java.util.ArrayList;
public class ArrayListRemoveByObjectExample {public static void main(String[] args) {ArrayList<String> fruits = new ArrayList<>();fruits.add("Apple");fruits.add("Banana");fruits.remove("Apple");System.out.println(fruits); }
}

通过remove(“Apple”)删除了ArrayList中值为"Apple"的元素,输出结果为[Banana]。

4.2.5遍历ArrayList

使用普通for循环
import java.util.ArrayList;
public class ArrayListForLoopExample {public static void main(String[] args) {ArrayList<String> fruits = new ArrayList<>();fruits.add("Apple");fruits.add("Banana");for (int i = 0; i < fruits.size(); i++) {System.out.println(fruits.get(i));}}
}

在这个循环中,i从0开始,每次循环增加1,直到i小于ArrayList的大小(通过size()方法获取)。在循环体内部,通过get(i)方法获取当前索引的元素并打印输出。

使用增强for循环(for - each循环)
import java.util.ArrayList;
public class ArrayListForEachLoopExample {public static void main(String[] args) {ArrayList<String> fruits = new ArrayList<>();fruits.add("Apple");fruits.add("Banana");for (String fruit : fruits) {System.out.println(fruit);}}
}

fruit变量会依次获取ArrayList中的每个元素,循环会自动遍历整个ArrayList,直到所有元素都被访问。

使用迭代器(Iterator):
import java.util.ArrayList;
import java.util.Iterator;
public class ArrayListIteratorExample {public static void main(String[] args) {ArrayList<String> fruits = new ArrayList<>();fruits.add("Apple");fruits.add("Banana");Iterator<String> iterator = fruits.iterator();while (iterator.hasNext()) {System.out.println(iterator.next());}}
}

通过iterator()方法获取ArrayList的迭代器。然后通过hasNext()方法检查是否还有下一个元素,如果有,则通过next()方法获取并打印下一个元素,直到没有更多元素为止。

五:ArrayList的扩容机制

ArrayList是⼀个动态类型的顺序表,即:在插⼊元素的过程中会⾃动扩容。

public boolean add(E e) {modCount++;add(e, elementData, size);return true;
}
private void add(E e, Object[] elementData, int s) {if (s == elementData.length) //第⼀次存储的时候 elementData.length=0,s = 0elementData = grow();elementData[s] = e;size = s + 1;
}
private Object[] grow() {return grow(size + 1);
}
private Object[] grow(int minCapacity) {int oldCapacity = elementData.length;if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {int newCapacity = ArraysSupport.newLength(oldCapacity,minCapacity - oldCapacity, /* minimum growth */oldCapacity >> 1 /* preferred growth */);return elementData = Arrays.copyOf(elementData, newCapacity);} else {return elementData = new Object[Math.max(DEFAULT_CAPACITY,minCapacity)];}
}
//oldLength: 当前数组的⻓度
//minGrowth: 最⼩需要增加的⻓度
//prefGrowth: ⾸选的增⻓ 度(通常是当前⼤⼩的⼀半)
public static int newLength(int oldLength, int minGrowth, int prefGrowth) {// preconditions not checked because of inlining// assert oldLength >= 0// assert minGrowth > 0//当前⻓度加上 minGrowth 和 prefGrowth 中的较⼤值。int prefLength = oldLength + Math.max(minGrowth, prefGrowth); // might overflowif (0 < prefLength && prefLength <= SOFT_MAX_ARRAY_LENGTH) {return prefLength;} else {// put code cold in a separate methodreturn hugeLength(oldLength, minGrowth);}
}
private static int hugeLength(int oldLength, int minGrowth) {int minLength = oldLength + minGrowth;if (minLength < 0) { // overflowthrow new OutOfMemoryError("Required array length " + oldLength + " + " + minGrowth + " is too large");} else if (minLength <= SOFT_MAX_ARRAY_LENGTH) {return SOFT_MAX_ARRAY_LENGTH;} else {return minLength;}
}

如果调⽤不带参数的构造⽅法,第⼀次分配数组⼤⼩为10
后续扩容为1.5倍扩容

这次的ArrayList的学习就到的了,剩下的叙述下一篇中
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/887459.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【第三方云音乐播放器SPlayer本地安装结合内网穿透打造个性化远程音乐库】

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” 文章目录 前言1. 安装Docker2. 创建并启动Splayer容器3. 本地访问测试4. 公网远程访问本地Splayer4.1 内网穿…

easyui combobox 只能选择第一个问题解决

easyui combobox 只能选择第一个问题解决 问题现象 在拆分开票的时候&#xff0c;弹出框上面有一个下拉框用于选择需要新增的明细行&#xff0c;但是每次只能选择到第一个 选择第二条数据的时候默认选择到第一个了 代码如下 /*新增发票编辑窗口*/function addTicketDialog…

从零开始:Linux 环境下的 C/C++ 编译教程

个人主页&#xff1a;chian-ocean 文章专栏 前言&#xff1a; GCC&#xff08;GNU Compiler Collection&#xff09;是一个功能强大的编译器集合&#xff0c;支持多种语言&#xff0c;包括 C 和 C。其中 gcc 用于 C 语言编译&#xff0c;g 专用于 C 编译。 Linux GCC or G的安…

transformer.js(三):底层架构及性能优化指南

Transformer.js 是一个轻量级、功能强大的 JavaScript 库&#xff0c;专注于在浏览器中运行 Transformer 模型&#xff0c;为前端开发者提供了高效实现自然语言处理&#xff08;NLP&#xff09;任务的能力。本文将详细解析 Transformer.js 的底层架构&#xff0c;并提供实用的性…

STM32 Keil5 attribute 关键字的用法

这篇文章记录一下STM32中attribute的用法。之前做项目的时候产品需要支持远程升级&#xff0c;要求版本只能向上迭代&#xff0c;不支持回退。当时想到的方案是把版本号放到bin文件的头部&#xff0c;设备端收到bin文件的首包部数据后判断是否满足升级要求&#xff0c;这里就可…

aws服务--机密数据存储KMS(1)介绍和使用

在AWS(Amazon Web Services)中存储机密数据时,安全性和合规性是最重要的考虑因素。AWS 提供了多个服务和工具,帮助用户确保数据的安全性、机密性以及合规性。AWS Secrets Manager、KMS(Key Management Service)是推荐的存储机密数据的AWS服务和最佳实践。这里先看KMS。 …

51c~C语言~合集2

我自己的原文哦~ https://blog.51cto.com/whaosoft/12652943 一、嵌入式开发中的C语言编译器 如果你和一个优秀的程序员共事&#xff0c;你会发现他对他使用的工具非常熟悉&#xff0c;就像一个画家了解他的画具一样。----比尔.盖茨1 不能简单的认为是个工具 嵌入式程序开发…

ensp静态路由实验

一、实验目的 1、熟练掌握交换机的基本配置命令 2、熟练掌握静态路由的使用方法 3. 熟练掌握交换机端口模式 二、实验内容 需求&#xff1a; 根据要求利用现有实验设备组建小型局域网 实验设备&#xff1a; 交换机S37002台&#xff1b;PC机2台&#xff1b;路由器2台。 …

深度学习3

五、自动微分 1、基础概念 模块 autograd 负责自动计算张量操作的梯度&#xff0c;具有自动求导功能&#xff1b;autograd 创建一个动态计算图来跟踪张量的操作&#xff0c;每个张量是计算图中的一个节点&#xff0c;节点之间的操作构成图的边。 属性 requires_grad 决定…

路由器中继与桥接

一 . 背景 现在的路由器大多数已经开始支持多种网络连接模式&#xff0c;以下将以TP-Link迷你无线路由器为例进行展开介绍。在TP-Link迷你无线路由器上一般有AP&#xff08;接入点&#xff09;模式&#xff0c;Router&#xff08;无线路由&#xff09;模式&#xff0c;Repeate…

人工智能|计算机视觉——微表情识别(Micro expression recognition)的研究现状

一、简述 微表情是一种特殊的面部表情,与普通的表情相比,微表情主要有以下特点: 持续时间短,通常只有1/25s~1/3s;动作强度低,难以察觉;在无意识状态下产生,通常难以掩饰或伪装;对微表情的分析通常需要在视频中,而普通表情在图像中就可以分析。由于微表情在无意识状态…

嵌入式系统与OpenCV

目录 一、OpenCV 简介 二、嵌入式 OpenCV 的安装方法 1. Ubuntu 系统下的安装 2. 嵌入式 ARM 系统中的安装 3. Windows10 和树莓派系统下的安装 三、嵌入式 OpenCV 的性能优化 1. 介绍嵌入式平台上对 OpenCV 进行优化的必要性。 2. 利用嵌入式开发工具&#xff0c;如优…

React(五)——useContecxt/Reducer/useCallback/useRef/React.memo/useMemo

文章目录 项目地址十六、useContecxt十七、useReducer十八、React.memo以及产生的问题18.1组件嵌套的渲染规律18.2 React.memo18.3 引出问题 十九、useCallback和useMemo19.1 useCallback对函数进行缓存19.2 useMemo19.2.1 基本的使用19.2.2 缓存属性数据 19.2.3 对于更新的理解…

STM32设计学生宿舍监测控制系统-分享

目录 前言 一、本设计主要实现哪些很“开门”功能&#xff1f; 二、电路设计原理图 电路图采用Altium Designer进行设计&#xff1a; 三、实物设计图 四、程序源代码设计 五、获取资料内容 前言 本项目旨在利用STM32单片机为核心&#xff0c;结合传感器技术、无线通信技…

华为无线AC+AP组网实际应用小结

之前公司都是使用的H3C的交换机、防火墙以及无线AC和AP的&#xff0c;最近优化下无线网络&#xff0c;说新的设备用华为的&#xff0c;然后我是直到要部署的当天才知道用华为设备的&#xff0c;就很无语了&#xff0c;一点准备没有&#xff0c;以下为这次的实际操作记录吧&…

Linux麦克风录音实战

在 Linux 上使用麦克风进行录音可以通过多种方式实现&#xff0c;包括使用命令行工具、图形界面应用程序以及编程接口。下面我将介绍几种常见的方法&#xff0c;从简单的命令行工具到使用 PortAudio 库进行编程。 一. 使用arecord命令行工具 arecord 是 ALSA&#xff08;Adva…

虚拟苹果系统MacOS中新建自定义C++Dylib并用C++测试程序测试

前言 苹果系统中Dylib的建立和使用是一个非常基础的功能。本博客使用苹果虚拟机MacOS Ventura 13.6.7&#xff0c;XCode15.2&#xff0c;来复现这个过程。供参考。 1、Dylib框架的建立 2、增加一个函数 注意&#xff0c;向导自动生成的Helloworld函数中嵌套了一个函数Helloworl…

Windows系统电脑安装TightVNC服务端结合内网穿透实现异地远程桌面

文章目录 前言1. 安装TightVNC服务端2. 局域网VNC远程测试3. Win安装Cpolar工具4. 配置VNC远程地址5. VNC远程桌面连接6. 固定VNC远程地址7. 固定VNC地址测试 前言 在追求高效、便捷的数字化办公与生活的今天&#xff0c;远程桌面服务成为了连接不同地点、不同设备之间的重要桥…

ThingsBoard规则链节点:Azure IoT Hub 节点详解

目录 引言 1. Azure IoT Hub 节点简介 2. 节点配置 2.1 基本配置示例 3. 使用场景 3.1 数据传输 3.2 数据分析 3.3 设备管理 4. 实际项目中的应用 4.1 项目背景 4.2 项目需求 4.3 实现步骤 5. 总结 引言 ThingsBoard 是一个开源的物联网平台&#xff0c;提供了设备…

如何利用 Puppeteer 的 Evaluate 函数操作网页数据

介绍 在现代的爬虫技术中&#xff0c;Puppeteer 因其强大的功能和灵活性而备受青睐。Puppeteer 是一个用于控制 Chromium 或 Chrome 浏览器的 Node.js 库&#xff0c;提供了丰富的 API 接口&#xff0c;能够帮助开发者高效地处理动态网页数据。本文将重点讲解 Puppeteer 的 ev…