数据结构之数组与链表的差异

news2024/11/28 17:53:54

一、数组

        数组(Array)是由相同类型的元素(element)的集合所组成的数据结构,分配一块连续的内存来存储。利用元素的索引(index)可以计算出该元素对应的存储地址。最简单的数据结构类型是一维数组。例如,索引为 0 到 9 的 32 位整数数组,可作为在存储器地址 2000,2004,2008,...2036 中,存储 10个 变量,因此索引为 i 的元素即在存储器中的 2000+4×i 地址。数组第一个元素的存储器地址称为第一地址或基础地址。

        简单来说,数组就是由一块连续的内存组成的数据结构。这个概念中有一个关键词“连续”,它反映了数组的一大特点,就是它必须是由一个连续的内存组成的,如下图所示。

数组添加元素的过程:

数组的优点

        数组的“连续”特征决定了它的访问速度很快,因为它是连续存储的,所以这就决定了它的存储位置就是固定的,因此它的访问速度就很快。比如现在有 10 个房间是按照年龄顺序入住的,当我们知道第一房子住的是 20 岁的人之后,那么我们就知道了第二个房子是 21 岁的人,第五个房子是 24 岁的人......等等。

数组的缺点

        祸兮福所倚,福兮祸所伏。数组的连续性既有优点又有缺点,优点上面已经说了,而缺点它对内存的要求比较高,必须要找到一块连续的内存才行。数组的另一个缺点就是插入和删除的效率比较慢,而且数组的大小固定,无法进行动态拓展。假如我们在数组的非尾部插入或删除一个数据,那么就要移动之后的所有数据,这就会带来一定的性能开销,删除的过程如下图所示:

二、链表

        链表是和数组互补的一种数据结构。链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer)。由于不必须按顺序存储,链表在插入的时候可以达到 O(1) 的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要 O(n) 的时间,而顺序表相应的时间复杂度分别是 O(logn) 和 O(1)。

        也就说链表是一个无需连续内存存储的数据结构,链表的元素有两个属性,一个是元素的,另一个是指针,此指针标记了下一个元素的地址。链表的数据结构,如下图所示:

链表添加的过程,如下图所示:

链表删除的过程,如下图所示:

链表主要分为单向链表、双向链表和循环链表

单向链表

        单向链表中包含两个域,一个信息域和一个指针域。这个链接指向列表中的下一个节点,而最后一个节点则指向一个空值,我们上面所展示的链表就是单向链表。

双向链表

        双向链表也叫双链表,双向链表中不仅有指向后一个节点的指针,还有指向前一个节点的指针,这样可以从任何一个节点访问前一个节点,当然也可以访问后一个节点,以至整个链表。双向链表的结构如下图所示:

循环链表

        循环链表中第一个节点之前就是最后一个节点,反之亦然。循环链表的无边界使得在这样的链表上设计算法会比普通链表更加容易。

循环链表的结构如下图所示:

 双向链表的由来

        有人可能会问,既然已经有单向链表了,那为什么还要双向链表呢?双向链表有什么优势呢?这个就要从链表的删除说起了,如果单向链表要删除元素的话,不但要找到删除的节点,还要找到删除节点的上一个节点(通常称之为前驱),因为需要变更上一个节点中 next 的指针,但又因为它是单向链表,所以在删除的节点中并没有存储上一个节点的相关信息,那么我们就需要再查询一遍链表以找到上一个节点,这样就带来了一定的性能问题,所以就有了双向链表。

链表优点

链表的优点大致可分为以下三个:

  1. 链表对内存的利用率比较高,无需连续的内存空间,即使有内存碎片,也不影响链表的创建;

  2. 链表的插入和删除的速度很快,无需像数组一样需要移动大量的元素;

  3. 链表大小不固定,可以很方便的进行动态扩展。

链表缺点

链表的主要缺点是不能随机查找,必须从第一个开始遍历,查找效率比较低,链表查询的时间复杂度是 O(n)。

三、性能评测

了解了数组和链表的基础知识之后,接下来我们正式进入性能评测环节。

在正式开始之前,我们先来明确一下测试目标,我们需要测试的点其实只有 6 个:

  • 头部/中间部分/尾部进行添加操作的性能测试;

  • 头部/中间部分/尾部开始查询的性能测试。

        因为添加操作和删除操作在执行时间层面基本是一致的,比如数组添加需要移动后面的元素,删除也同样是移动后面的元素;而链表也是如此,添加和删除都是改变自身和相连节点的信息,因此我们就把添加和删除的测试合二为一,用添加操作来进行测试。

1.头部添加性能测试

import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.concurrent.TimeUnit;

@BenchmarkMode(Mode.AverageTime) // 测试完成时间
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 2, time = 1, timeUnit = TimeUnit.SECONDS) // 预热次数和时间
@Measurement(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS) // 测试次数和时间
@Fork(1) // fork 1 个线程
@State(Scope.Thread)
public class ArrayOptimizeTest {

    private static final int maxSize = 1000; // 测试循环次数
    private static final int operationSize = 100; // 操作次数


    private static ArrayList<Integer> arrayList;
    private static LinkedList<Integer> linkedList;

    public static void main(String[] args) throws RunnerException {
        // 启动基准测试
        Options opt = new OptionsBuilder()
                .include(ArrayOptimizeTest.class.getSimpleName()) // 要导入的测试类
                .build();
        new Runner(opt).run(); // 执行测试
    }

    @Setup
    public void init() {
        // 启动执行事件
        arrayList = new ArrayList<Integer>();
        linkedList = new LinkedList<Integer>();
        for (int i = 0; i < maxSize; i++) {
            arrayList.add(i);
            linkedList.add(i);
        }
    }

 @Benchmark
    public void addArrayByFirst(Blackhole blackhole) {
        for (int i = 0; i < +operationSize; i++) {
            arrayList.add(i, i);
        }
        // 为了避免 JIT 忽略未被使用的结果计算
        blackhole.consume(arrayList);
    }

    @Benchmark
    public void addLinkedByFirst(Blackhole blackhole) {
        for (int i = 0; i < +operationSize; i++) {
            linkedList.add(i, i);
        }
        // 为了避免 JIT 忽略未被使用的结果计算
        blackhole.consume(linkedList);
    }
}

从以上代码可以看出,在测试之前,我们先将 ArrayList 和 LinkedList 进行数据初始化,再从头部开始添加 100 个元素,执行结果如下:

图片

从以上结果可以看出,LinkedList 的平均执行(完成)时间比 ArrayList 平均执行时间快了约 216 倍。

2.中间添加性能测试

import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.concurrent.TimeUnit;

@BenchmarkMode(Mode.AverageTime) // 测试完成时间
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 2, time = 1, timeUnit = TimeUnit.SECONDS) // 预热次数和时间
@Measurement(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS) // 测试次数和时间
@Fork(1) // fork 1 个线程
@State(Scope.Thread)
public class ArrayOptimizeTest {
    private static final int maxSize = 1000; // 测试循环次数
    private static final int operationSize = 100; // 操作次数

    private static ArrayList<Integer> arrayList;
    private static LinkedList<Integer> linkedList;

    public static void main(String[] args) throws RunnerException {
        // 启动基准测试
        Options opt = new OptionsBuilder()
                .include(ArrayOptimizeTest.class.getSimpleName()) // 要导入的测试类
                .build();
        new Runner(opt).run(); // 执行测试
    }

    @Setup
    public void init() {
        // 启动执行事件
        arrayList = new ArrayList<Integer>();
        linkedList = new LinkedList<Integer>();
        for (int i = 0; i < maxSize; i++) {
            arrayList.add(i);
            linkedList.add(i);
        }
    }
    
    @Benchmark
    public void addArrayByMiddle(Blackhole blackhole) {
        int startCount = maxSize / 2; // 计算中间位置
        // 中间部分进行插入
        for (int i = startCount; i < (startCount + operationSize); i++) {
            arrayList.add(i, i);
        }
        // 为了避免 JIT 忽略未被使用的结果计算
        blackhole.consume(arrayList);
    }

    @Benchmark
    public void addLinkedByMiddle(Blackhole blackhole) {
        int startCount = maxSize / 2; // 计算中间位置
        // 中间部分进行插入
        for (int i = startCount; i < (startCount + operationSize); i++) {
            linkedList.add(i, i);
        }
        // 为了避免 JIT 忽略未被使用的结果计算
        blackhole.consume(linkedList);
    }
}

从以上代码可以看出,在测试之前,我们先将 ArrayList 和 LinkedList 进行数据初始化,再从中间开始添加 100 个元素,执行结果如下:

图片

从上述结果可以看出,LinkedList 的平均执行时间比 ArrayList 平均执行时间快了约 54 倍。

3.尾部添加性能测试

import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.concurrent.TimeUnit;

@BenchmarkMode(Mode.AverageTime) // 测试完成时间
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 2, time = 1, timeUnit = TimeUnit.SECONDS) // 预热次数和时间
@Measurement(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS) // 测试次数和时间
@Fork(1) // fork 1 个线程
@State(Scope.Thread)
public class ArrayOptimizeTest {
    private static final int maxSize = 1000; // 测试循环次数
    private static final int operationSize = 100; // 操作次数

    private static ArrayList<Integer> arrayList;
    private static LinkedList<Integer> linkedList;

    public static void main(String[] args) throws RunnerException {
        // 启动基准测试
        Options opt = new OptionsBuilder()
                .include(ArrayOptimizeTest.class.getSimpleName()) // 要导入的测试类
                .build();
        new Runner(opt).run(); // 执行测试
    }

    @Setup
    public void init() {
        // 启动执行事件
        arrayList = new ArrayList<Integer>();
        linkedList = new LinkedList<Integer>();
        for (int i = 0; i < maxSize; i++) {
            arrayList.add(i);
            linkedList.add(i);
        }
    }
    
    @Benchmark
    public void addArrayByEnd(Blackhole blackhole) {
        int startCount = maxSize - 1 - operationSize;
        for (int i = startCount; i < (maxSize - 1); i++) {
            arrayList.add(i, i);
        }
        // 为了避免 JIT 忽略未被使用的结果计算
        blackhole.consume(arrayList);
    }

    @Benchmark
    public void addLinkedByEnd(Blackhole blackhole) {
        int startCount = maxSize - 1 - operationSize;
        for (int i = startCount; i < (maxSize - 1); i++) {
            linkedList.add(i, i);
        }
        // 为了避免 JIT 忽略未被使用的结果计算
        blackhole.consume(linkedList);
    }
}

以上程序的执行结果为:

图片

从上述结果可以看出,LinkedList 的平均执行时间比 ArrayList 平均执行时间快了约 32 倍。

4.头部查询性能评测

import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.concurrent.TimeUnit;

@BenchmarkMode(Mode.AverageTime) // 测试完成时间
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 2, time = 1, timeUnit = TimeUnit.SECONDS) // 预热次数和时间
@Measurement(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS) // 测试次数和时间
@Fork(1) // fork 1 个线程
@State(Scope.Thread)
public class ArrayOptimizeTest {
    private static final int maxSize = 1000; // 测试循环次数
    private static final int operationSize = 100; // 操作次数

    private static ArrayList<Integer> arrayList;
    private static LinkedList<Integer> linkedList;

    public static void main(String[] args) throws RunnerException {
        // 启动基准测试
        Options opt = new OptionsBuilder()
                .include(ArrayOptimizeTest.class.getSimpleName()) // 要导入的测试类
                .build();
        new Runner(opt).run(); // 执行测试
    }

    @Setup
    public void init() {
        // 启动执行事件
        arrayList = new ArrayList<Integer>();
        linkedList = new LinkedList<Integer>();
        for (int i = 0; i < maxSize; i++) {
            arrayList.add(i);
            linkedList.add(i);
        }
    }

    @Benchmark
    public void findArrayByFirst() {
        for (int i = 0; i < operationSize; i++) {
            arrayList.get(i);
        }
    }

    @Benchmark
    public void findLinkedyByFirst() { 
        for (int i = 0; i < operationSize; i++) {
            linkedList.get(i);
        }
    }
}

以上程序的执行结果为:

图片

从上述结果可以看出,从头部查询 100 个元素时 ArrayList 的平均执行时间比 LinkedList 平均执行时间快了约 1990 倍。

5.中间查询性能评测

import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.concurrent.TimeUnit;

@BenchmarkMode(Mode.AverageTime) // 测试完成时间
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 2, time = 1, timeUnit = TimeUnit.SECONDS) // 预热次数和时间
@Measurement(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS) // 测试次数和时间
@Fork(1) // fork 1 个线程
@State(Scope.Thread)
public class ArrayOptimizeTest {
    private static final int maxSize = 1000; // 测试循环次数
    private static final int operationSize = 100; // 操作次数

    private static ArrayList<Integer> arrayList;
    private static LinkedList<Integer> linkedList;

    public static void main(String[] args) throws RunnerException {
        // 启动基准测试
        Options opt = new OptionsBuilder()
                .include(ArrayOptimizeTest.class.getSimpleName()) // 要导入的测试类
                .build();
        new Runner(opt).run(); // 执行测试
    }

    @Setup
    public void init() {
        // 启动执行事件
        arrayList = new ArrayList<Integer>();
        linkedList = new LinkedList<Integer>();
        for (int i = 0; i < maxSize; i++) {
            arrayList.add(i);
            linkedList.add(i);
        }
    }

    @Benchmark
    public void findArrayByMiddle() { 
        int startCount = maxSize / 2;
        int endCount = startCount + operationSize;
        for (int i = startCount; i < endCount; i++) {
            arrayList.get(i);
        }
    }

    @Benchmark
    public void findLinkedyByMiddle() { 
        int startCount = maxSize / 2;
        int endCount = startCount + operationSize;
        for (int i = startCount; i < endCount; i++) {
            linkedList.get(i);
        }
    }
}

以上程序的执行结果为:

图片

从上述结果可以看出,从中间查询 100 个元素时 ArrayList 的平均执行时间比 LinkedList 平均执行时间快了约 28089 倍,真是恐怖。

6.尾部查询性能评测

import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.concurrent.TimeUnit;

@BenchmarkMode(Mode.AverageTime) // 测试完成时间
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 2, time = 1, timeUnit = TimeUnit.SECONDS) // 预热次数和时间
@Measurement(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS) // 测试次数和时间
@Fork(1) // fork 1 个线程
@State(Scope.Thread)
public class ArrayOptimizeTest {
    private static final int maxSize = 1000; // 测试循环次数
    private static final int operationSize = 100; // 操作次数

    private static ArrayList<Integer> arrayList;
    private static LinkedList<Integer> linkedList;

    public static void main(String[] args) throws RunnerException {
        // 启动基准测试
        Options opt = new OptionsBuilder()
                .include(ArrayOptimizeTest.class.getSimpleName()) // 要导入的测试类
                .build();
        new Runner(opt).run(); // 执行测试
    }

    @Setup
    public void init() {
        // 启动执行事件
        arrayList = new ArrayList<Integer>();
        linkedList = new LinkedList<Integer>();
        for (int i = 0; i < maxSize; i++) {
            arrayList.add(i);
            linkedList.add(i);
        }
    }

    @Benchmark
    public void findArrayByEnd() {
        for (int i = (maxSize - operationSize); i < maxSize; i++) {
            arrayList.get(i);
        }
    }

    @Benchmark
    public void findLinkedyByEnd() { 
        for (int i = (maxSize - operationSize); i < maxSize; i++) {
            linkedList.get(i);
        }
    }
}

以上程序的执行结果为:

图片

从上述结果可以看出,从尾部查询 100 个元素时 ArrayList 的平均执行时间比 LinkedList 平均执行成时间快了约 1839 倍。

7.扩展添加测试

接下来我们再来测试一下,正常情况下我们从头开始添加数组和链表的性能对比,测试代码如下:

import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.concurrent.TimeUnit;

@BenchmarkMode(Mode.AverageTime) // 测试完成时间
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 2, time = 1, timeUnit = TimeUnit.SECONDS) // 预热次数和时间
@Measurement(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS) // 测试次数和时间
@Fork(1) // fork 1 个线程
@State(Scope.Thread)
public class ArrayOptimizeTest {

    private static final int maxSize = 1000; // 测试循环次数

    private static ArrayList<Integer> arrayList;
    private static LinkedList<Integer> linkedList;

    public static void main(String[] args) throws RunnerException {
        // 启动基准测试
        Options opt = new OptionsBuilder()
                .include(ArrayOptimizeTest.class.getSimpleName()) // 要导入的测试类
                .build();
        new Runner(opt).run(); // 执行测试
    }
    
    @Benchmark
    public void addArray(Blackhole blackhole) { // 中间删数组表
        arrayList = new ArrayList<Integer>();
        for (int i = 0; i < maxSize; i++) {
            arrayList.add(i);
        }
        // 为了避免 JIT 忽略未被使用的结果计算
        blackhole.consume(arrayList);
    }

    @Benchmark
    public void addLinked(Blackhole blackhole) { // 中间删除链表
        linkedList = new LinkedList<Integer>();
        for (int i = 0; i < maxSize; i++) {
            linkedList.add(i);
        }
        // 为了避免 JIT 忽略未被使用的结果计算
        blackhole.consume(linkedList);
    }
}

以上程序的执行结果为:

图片

接下来,我们将添加的次数调至 1w,测试结果如下:

图片

最后,我们再将添加次数调至 10w,测试结果如下:

图片

从以上结果可以看出在正常情况下,从头部依次开始添加元素时,他们性能差别不大。

四、总 结

        本文我们介绍了数组的概念以及它的优缺点,同时还介绍了单向链表、双向链表及循环链表的概念以及链表的优缺点。我们在最后的评测中可以看出,当我们正常从头部依次添加元素时,链表和数组的性能差不不大。但当数据初始化完成之后,我们再进行插入操作时,尤其是从头部插入时,因为数组要移动之后的所有元素,因此性能要比链表低很多;但在查询时性能刚好相反,因为链表要遍历查询,并且 LinkedList 是双向链表,所以在中间查询时性能要比数组查询慢了上万倍(查询 100 个元素),而两头查询(首部和尾部)时,链表也比数组慢了将近 1000 多倍(查询 100 个元素),因此在查询比较多的场景中,我们要尽量使用数组,而在添加和删除操作比较多时,我们应该使用链表结构

数组和链表的操作时间复杂度,如下表所示:

数组链表
查询O(1)O(n)
插入O(n)O(1)
删除O(n)O(1)

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

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

相关文章

RabbitMQ7:消息转换器

欢迎来到“雪碧聊技术”CSDN博客&#xff01; 在这里&#xff0c;您将踏入一个专注于Java开发技术的知识殿堂。无论您是Java编程的初学者&#xff0c;还是具有一定经验的开发者&#xff0c;相信我的博客都能为您提供宝贵的学习资源和实用技巧。作为您的技术向导&#xff0c;我将…

Ubuntu20.04+ROS 进行机械臂抓取仿真:环境搭建(一)

目录 一、从官网上下载UR机械臂 二、给UR机械臂添加夹爪 三、报错解决 本文详细介绍如何在Ubuntu20.04ROS环境中为Universal Robots的UR机械臂添加夹爪。首先从官方和第三方源下载必要的软件包&#xff0c;包括UR机械臂驱动、夹爪插件和相关依赖。然后&#xff0c;针对gazeb…

(即插即用模块-Attention部分) 二十、(2021) GAA 门控轴向注意力

文章目录 1、Gated Axial-Attention2、代码实现 paper&#xff1a;Medical Transformer: Gated Axial-Attention for Medical Image Segmentation Code&#xff1a;https://github.com/jeya-maria-jose/Medical-Transformer 1、Gated Axial-Attention 论文首先分析了 ViTs 在训…

Git 进程占用报错-解决方案

背景 大仓库&#xff0c;由于开发者分支较多&#xff0c;我们在使用 git pull 或 git push 等命令时&#xff08;与远端仓库交互的命令&#xff09;&#xff0c;不知之前配置了什么&#xff0c;我的电脑会必现以下报错&#xff08;有非常长一大串报错-不同分支的git进程占用报…

【FPGA-MicroBlaze】串口收发以及相关函数讲解

前言 工具&#xff1a;Vivado2018.3及其所对应的SDK版本 目前网上有许多MicroBlaze 的入门教程&#xff0c;比如下面的这个参考文章&#xff0c;用串口打印一个hello world。 【FPGA】Xilinx MicroBlaze软核使用第一节&#xff1a;Hello World!_fpga软核microblaze-CSDN博客 个…

代码美学2:MATLAB制作渐变色

效果&#xff1a; %代码美学&#xff1a;MATLAB制作渐变色 % 创建一个10x10的矩阵来表示热力图的数据 data reshape(1:100, [10, 10]);% 创建热力图 figure; imagesc(data);% 设置颜色映射为“cool” colormap(cool);% 在热力图上添加边框 axis on; grid on;% 设置热力图的颜色…

Android下载出现open failed: EPERM (Operation not permitted)

今天帮忙给同事调一下apk&#xff0c;发现android 自动更新apk&#xff0c;下载apk的时候总是失败&#xff0c;总是卡在 输出流这一步了 于是第一步分析&#xff0c;立马想到权限 但是下载之前的读写内存的权限也都有了 什么android 10高版本的不开启分区存储也用了 android…

使用爬虫时,如何确保数据的准确性?

在数字化时代&#xff0c;数据的准确性对于决策和分析至关重要。本文将探讨如何在使用Python爬虫时确保数据的准确性&#xff0c;并提供代码示例。 1. 数据清洗 数据清洗是确保数据准确性的首要步骤。在爬取数据后&#xff0c;需要对数据进行清洗&#xff0c;去除重复、无效和…

uniapp中使用Mescroll实现下拉刷新与上拉加载项目实战

如何在UniApp中使用Mescroll实现下拉刷新与上拉加载 前言 下拉刷新和上拉加载更多成为了提升用户体验不可或缺的功能。UniApp作为一个跨平台的应用开发框架&#xff0c;支持使用Vue.js语法编写多端&#xff08;iOS、Android、H5等&#xff09;应用。Mescroll作为一款专为Vue设…

【接口自动化测试】一文从0到1详解接口测试协议!

接口自动化测试是软件开发过程中重要的环节之一。通过对接口进行测试&#xff0c;可以验证接口的功能和性能&#xff0c;确保系统正常运行。本文将从零开始详细介绍接口测试的协议和规范。 定义接口测试协议 接口测试协议是指用于描述接口测试的规范和约定。它包含了接口的请求…

RAG数据拆分之PDF

引言RAG数据简介PDF解析方法及工具代码实现总结 二、正文内容 引言 本文将介绍如何将RAG数据拆分至PDF格式&#xff0c;并探讨PDF解析的方法和工具&#xff0c;最后提供代码示例。 RAG数据简介 RAG&#xff08;关系型属性图&#xff09;是一种用于表示实体及其关系的图数据…

【开源项目】2024最新PHP在线客服系统源码/带预知消息/带搭建教程

简介 随着人工智能技术的飞速发展&#xff0c;AI驱动的在线客服系统已经成为企业提升客户服务质量和效率的重要工具。本文将探讨AI在线客服系统的理论基础&#xff0c;并展示如何使用PHP语言实现一个简单的AI客服系统。源码仓库地址&#xff1a;ym.fzapp.top 在线客服系统的…

WEB攻防-通用漏洞XSS跨站MXSSUXSSFlashXSSPDFXSS

演示案例&#xff1a; UXSS-Edge&CVE-2021-34506 FlashXSS-PHPWind&SWF反编译 PDFXSS-PDF动作添加&文件上传 使用jpexs反编译swf文件 上传后&#xff0c;发给别人带漏洞的分享链接

QSqlTableModel的使用

实例功能 这边使用一个实例显示数据库 demodb 中 employee 数据表的内容&#xff0c;实现编辑、插入、删除的操作&#xff0c;实现数据的排序和记录过滤&#xff0c;还实现 BLOB 类型字段 Photo 中存储照片的显示、导入等操作&#xff0c;运行界面如下图&#xff1a; 在上图中…

适用于学校、医院等低压用电场所的智能安全配电装置

引言 电力&#xff0c;作为一种清洁且高效的能源&#xff0c;极大地促进了现代生活的便捷与舒适。然而&#xff0c;与此同时&#xff0c;因使用不当或维护缺失等问题&#xff0c;漏电、触电事件以及电气火灾频发&#xff0c;对人们的生命安全和财产安全构成了严重威胁&#xf…

LabVIEW实现UDP通信

目录 1、UDP通信原理 2、硬件环境部署 3、云端环境部署 4、UDP通信函数 5、程序架构 6、前面板设计 7、程序框图设计 8、测试验证 本专栏以LabVIEW为开发平台&#xff0c;讲解物联网通信组网原理与开发方法&#xff0c;覆盖RS232、TCP、MQTT、蓝牙、Wi-Fi、NB-IoT等协议。 结合…

java——Spring MVC的工作流程

Spring MVC的工作流程是基于模型-视图-控制器&#xff08;MVC&#xff09;设计模式的一个典型实现&#xff0c;以下是其主要工作流程步骤&#xff1a; 客户端请求提交&#xff1a; 用户通过浏览器向服务器发送请求&#xff0c;该请求首先到达Spring MVC的前端控制器DispatcherS…

带有悬浮窗功能的Android应用

android api29 gradle 8.9 要求 布局文件 (floating_window_layout.xml): 增加、删除、关闭按钮默认隐藏。使用“开始”按钮来控制这些按钮的显示和隐藏。 服务类 (FloatingWindowService.kt): 实现“开始”按钮的功能&#xff0c;点击时切换增加、删除、关闭按钮的可见性。处…

【编译原理】词法、语法、语义实验流程内容梳理

编译原理实验有点难&#xff0c;但是加上ai的辅助就会很简单&#xff0c;下面梳理一下代码流程。 全代码在github仓库中&#xff0c;链接&#xff1a;NeiFeiTiii/CompilerOriginTest at Version2.0&#xff0c;感谢star一下 一、项目结构 关键内容就是里面的那几个.c和.h文件。…

Linux介绍与安装指南:从入门到精通

1. Linux简介 1.1 什么是Linux&#xff1f; Linux是一种基于Unix的操作系统&#xff0c;由Linus Torvalds于1991年首次发布。Linux的核心&#xff08;Kernel&#xff09;是开源的&#xff0c;允许任何人自由使用、修改和分发。Linux操作系统通常包括Linux内核、GNU工具集、图…