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

news2024/11/27 6:07:09

在这里插入图片描述

我的个人主页
我的专栏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 = 0
	elementData = 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 overflow
	if (0 < prefLength && prefLength <= SOFT_MAX_ARRAY_LENGTH) {
		return prefLength;
	} else {
		// put code cold in a separate method
			return hugeLength(oldLength, minGrowth);
		}
}
private static int hugeLength(int oldLength, int minGrowth) {
	int minLength = oldLength + minGrowth;
	if (minLength < 0) { // overflow
		throw 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.coloradmin.cn/o/2248228.html

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

相关文章

【k8s】资源限制管理:Namespace、Deployment与Pod的实践

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《Kubernetes航线图&#xff1a;从船长到K8s掌舵者》 &#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、引言 1、什么是k8s 2、在k8s使用资源配额的作…

lua除法bug

故事背景&#xff0c;新来了一个数值&#xff0c;要改公式。神奇的一幕出现了&#xff0c;公式算出一个非常大的数。排查是lua有一个除法bug,1除以大数得到一个非常大的数。 function div(a, b)return tonumber(string.format("%.2f", a/b)) end print(1/73003) pri…

微信小程序学习指南从入门到精通

&#x1f5fd;微信小程序学习指南从入门到精通&#x1f5fd; &#x1f51d;微信小程序学习指南从入门到精通&#x1f51d;✍前言✍&#x1f4bb;微信小程序学习指南前言&#x1f4bb;一、&#x1f680;文章列表&#x1f680;二、&#x1f52f;教程文章的好处&#x1f52f;1. ✅…

《基于FPGA的便携式PWM方波信号发生器》论文分析(三)——数码管稳定显示与系统调试

一、论文概述 基于FPGA的便携式PWM方波信号发生器是一篇由任青颖、庹忠曜、黄洵桢、李智禺和张贤宇 等人发表的一篇期刊论文。该论文主要研究了一种新型的信号发生器&#xff0c;旨在解决传统PWM信号发生器在移动设备信号调控中存在的精准度低和便携性差的问题 。其基于现场可编…

计算机操作系统——进程控制(Linux)

进程控制 进程创建fork&#xff08;&#xff09;函数fork() 的基本功能fork() 的基本语法fork() 的工作原理fork() 的典型使用示例fork() 的常见问题fork() 和 exec() 结合使用总结 进程终止与$进程终止的本质进程终止的情况正常退出&#xff08;Exit&#xff09;由于信号终止非…

【贪心算法第四弹——376.摆动序列】

目录 1.题目解析 题目来源 测试用例 2.算法原理 3.实战代码 代码解析 本题还可以使用动态规划的解法来解决&#xff0c;不过动态规划的时间复杂度为O(N^2)&#xff0c;而贪心解法的时间复杂度为O(N)&#xff0c;动态规划方法的博客链接: 动态规划-子序列问题——376.摆动…

我谈离散傅里叶变换的补零

有限序列的零延拓——零延拓不会改变离散傅里叶变换的形状的续篇。 L点序列可以做N点傅里叶变换&#xff0c;当 L ⩽ N L\leqslant N L⩽N时不会产生混叠。这部分内容在Rafael Gonzalez和Richard Woods所著的《数字图像处理》完全没有提到。 补零是序列末尾补零&#xff0c;不…

day18 结构体

有参宏和函数的区别 1.展开时机&#xff1a;有参宏而言&#xff0c;在预处理阶段展开&#xff0c;而函数在调用时才展开 2.内存使用&#xff1a;有参宏而言&#xff0c;占用的是所在函数的空间&#xff0c;而函数在调用时会单独开辟空间 3.效率上&#xff1a;有参宏的效率比…

C嘎嘎探索篇:栈与队列的交响:C++中的结构艺术

C嘎嘎探索篇&#xff1a;栈与队列的交响&#xff1a;C中的结构艺术 前言&#xff1a; 小编在之前刚完成了C中栈和队列&#xff08;stack和queue&#xff09;的讲解&#xff0c;忘记的小伙伴可以去我上一篇文章看一眼的&#xff0c;今天小编将会带领大家吹奏栈和队列的交响&am…

Postman设置接口关联,实现参数化

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 postman设置接口关联 在实际的接口测试中&#xff0c;后一个接口经常需要用到前一个接口返回的结果&#xff0c; 从而让后一个接口能正常执行&#xff0c;这…

大模型的RAG微调与Agent:提升智能代理的效率与效果

目录 ​编辑 引言 RAG模型概述 检索阶段 生成阶段 RAG模型的微调 数据集选择 损失函数设计 微调策略 超参数调整 RAG模型在智能代理中的应用 客户服务 信息检索 内容创作 决策支持&#xff1a; 结论 引言 在人工智能的快速发展中&#xff0c;大型预训练模型&a…

前端---CSS(部分用法)

HTML画页面--》这个页面就是页面上需要的元素罗列起来&#xff0c;但是页面效果很差&#xff0c;不好看&#xff0c;为了让页面好看&#xff0c;为了修饰页面---》CSS CSS的作用&#xff1a;修饰HTML页面 用了CSS之后&#xff0c;样式和元素本身做到了分离的效果。---》降低了代…

【R语言管理】Pycharm配置R语言及使用Anaconda管理R语言虚拟环境

目录 使用Anaconda创建R语言虚拟环境1. 安装Anaconda2. 创建R语言虚拟环境 Pycharm配置R语言1. 安装Pycharm2. R Language for IntelliJ插件 参考 使用Anaconda创建R语言虚拟环境 1. 安装Anaconda Anaconda的安装可参见另一博客-【Python环境管理工具】Anaconda安装及使用教程…

互联网视频推拉流EasyDSS视频直播点播平台视频转码有哪些技术特点和应用?

视频转码本质上是一个先解码再编码的过程。在转码过程中&#xff0c;原始视频码流首先被解码成原始图像数据&#xff0c;然后再根据目标编码标准、分辨率、帧率、码率等参数重新进行编码。这样&#xff0c;转换前后的码流可能遵循相同的视频编码标准&#xff0c;也可能不遵循。…

Linux Shell 脚本题目集

1、执行 ping 命令对指定主机进行测试&#xff0c;以确定该主机是否处于存活状态并输出相应结果。 #!/bin/bashread -p "请输入主机号&#xff1a;" pc # 读取用户输入的主机号if [ -z "$pc" ];then # 检查用户输入是否为空echo "主…

使用ENSP实现默认路由

一、项目拓扑 二、项目实现 1.路由器AR1配置 进入系统试图 sys将路由器命名为R1 sysname R1关闭信息中心 undo info-center enable 进入g0/0/0接口 int g0/0/0将g0/0/0接口IP地址配置为2.2.2.1/24 ip address 2.2.2.1 24进入g0/0/1接口 int g0/0/1将g0/0/1接口IP地址配置为1.…

【vue3实现微信小程序】每日专题与分页跳转的初步实现

快速跳转&#xff1a; 我的个人博客主页&#x1f449;&#xff1a;Reuuse博客 新开专栏&#x1f449;&#xff1a;Vue3专栏 参考文献&#x1f449;&#xff1a;uniapp官网 免费图标&#x1f449;&#xff1a;阿里巴巴矢量图标库 ❀ 感谢支持&#xff01;☀ 前情提要 &#x…

小程序-基于java+SpringBoot+Vue的网上花店微信小程序设计与实现

项目运行 1.运行环境&#xff1a;最好是java jdk 1.8&#xff0c;我们在这个平台上运行的。其他版本理论上也可以。 2.IDE环境&#xff1a;IDEA&#xff0c;Eclipse,Myeclipse都可以。推荐IDEA; 3.tomcat环境&#xff1a;Tomcat 7.x,8.x,9.x版本均可 4.硬件环境&#xff1a…

Opencv+ROS实现颜色识别应用

目录 一、工具 二、原理 概念 本质 三、实践 添加发布话题 主要代码 四、成果 五、总结 一、工具 opencvros ubuntu18.04 摄像头 二、原理 概念 彩色图像&#xff1a;RGB&#xff08;红&#xff0c;绿&#xff0c;蓝&#xff09; HSV图像&#xff1a;H&#xff0…

解决 java -jar 报错:xxx.jar 中没有主清单属性

问题复现 在使用 java -jar xxx.jar 命令运行 Java 应用程序时&#xff0c;遇到了以下错误&#xff1a; xxx.jar 中没有主清单属性这个错误表示 JAR 文件缺少必要的启动信息&#xff0c;Java 虚拟机无法找到应用程序的入口点。本文将介绍该错误的原因以及如何通过修改 pom.xm…