【数据结构】线性表( List)和 顺序表(ArrayList)
【数据结构】线性表( List)和 顺序表(ArrayList)
- 一、线性表 List
- 二、List 接口的常用方法
- 三、ArrayList与顺序表
- 3.1 引入顺序表的原因?
- 3.2 ArrayList 的使用
- 3.2.1 ArrayList 的创建
- 3.2.2 添加元素:list.add( )
- 3.2.3 获取List元素个数:list.size()
- 3.2.4 获取List 中的元素 / 设置 List 中的元素:list.get()
- 3.2.4 修改 List 中的元素:list.set()
- 3.2.5 遍历 List 中的元素:下标 + for each / 迭代器
- 3.2.6 在指定位置,添加元素:list.add(1,"111")
- 3.2.7 删除元素: list.remove( )
- 3.2.8 判断元素是否存在:contains
- 3.2.9 查询元素的位置:indexOf / lastIndexOf
- 3.2.10 获取顺序表中的 “子列表” : sublist
- 3.2.11 清空整个顺序表: clear
- 3.2.12 ArrayList 的扩容机制
- 四、 ArrayList 的具体使用
- 4.1 洗牌和发牌 算法
- 4.2 杨辉三角
- 五、 基于数组,自己实现 ArrayList
一、线性表 List
- 线性表定义:具有多个元素,且元素之间为一个挨着一个。
- 线性表特点:每个元素,都只有一个前驱,一个后继。
3. 线性表再往下细分,又分为两种实现方式:
- 顺序表 ==> 数组(经过封装后的数组,就称为顺序表)
- 链表
顺序表和链表 最本质的区别:
(1) 顺序表上的元素 在连续的内存空间上(物理上和逻辑上都是线性的)
(2) 链表上的元素,则不要求连续。通过一些其他的方式,来把前驱和后继联系起来。
(逻辑上是线性的,有前有后;物理上,放在离散的空间也可以)
- 在集合框架中,List 是⼀个interface(接⼝),继承自Collection(Collection 是标准库里内置的高级接口)。
List 定义了一个线性表,应该支持哪些功能,这些具体的功能,交给实现 List 的类来完成。
Java 中,实现 List 的类,核心是两个:
(1)ArrayList 对应到 顺序表
(2)LinkedList 对应到 链表
学习 数据结构中 的 顺序表 和 链表,分别对应 Java 中 ArrayList 和 LinkedList 这两个类的使用。
二、List 接口的常用方法
和 StringBuilder 很相似,StringBuilder 管理的是一系列的 char,而此处 List 是泛型的,可以管理任何需要的对象。
三、ArrayList与顺序表
3.1 引入顺序表的原因?
- 对于数组来说,只能通过 [ ] 数组下标 来 取 / 修改 元素,.length 获取长度。
- 而我们希望顺序表能够具备更多的功能:将下面的方法写一个类,把数组包裹一下,后续再给这个类提供这些方法的实现,更方便得使用这个顺序表。
- 所以,在以后的开发中,很少直接使用数组,能用ArrayList 就用 ArrayList。
3.2 ArrayList 的使用
3.2.1 ArrayList 的创建
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;public class Demo4 {//ArrayList的使用public static void main(String[] args) {//创建 ArrayList 对象,构造空的顺序表//(1)创建ArrayList对象,方式1ArrayList<String> arrayList= new ArrayList<>();//空盒子,盒子里没有东西,盒子本体还在//ArrayList就是带有泛型参数的类//创建ArrayList对象,就根据泛型的语法创建ArrayList<String> arrayList2 = null;//(3)连盒子都没有//(2)创建ArrayList对象,方式2List<String> list= new ArrayList<>();//向上转型//ArrayList类定义的时候,实现了List 接口//完全可以使用 List引用 指向 ArrayList 实例,这就是向上转型//(4)使用 arrayList 复制一份,生成 arrayList3ArrayList<String> arrayList3 = new ArrayList<>(arrayList);//(5)构造ArrayList的同时,可以取指定初始容量ArrayList<String> arrayList4 = new ArrayList<>(10);//10 :当前构造出的 ArrayList,初始容量是10个元素//区别于数组:数组,创建好之后,容量就固定了,new int[];//而ArrayList可以动态扩容,只要机器的内存足够用,就能一直持续扩容,保证元素否能被容纳进去(顺序表的一个核心功能)}
}
3.2.2 添加元素:list.add( )
import java.util.ArrayList;
import java.util.List;public class Demo5 {public static void main(String[] args) {//(6) .add(): 往ArrayList 添加元素List<String> list = new ArrayList<>();list.add("aaa");list.add("bbb");list.add("ccc");System.out.println(list);//[aaa, bbb, ccc]//Java中,集合类一般都可以直接打印//add这样的方法,内部就会涉及到自动扩容}
}
3.2.3 获取List元素个数:list.size()
import java.util.ArrayList;
import java.util.List;public class Demo6 {public static void main(String[] args) {List<String> list = new ArrayList<>();//(7)获取元素个数//数组提供了.length 属性,获取到元素个数;//ArrayList 也提供了.size()方法,获取元素个数list.add("aaa");list.add("bbb");list.add("ccc");System.out.println(list.size());//3}
}
数组通过 .length 属性 获取长度;
String 是通过 .length() 方法获取长度;
集合类(不仅仅是List)是通过.size() 方法 获取长度。
3.2.4 获取List 中的元素 / 设置 List 中的元素:list.get()
import java.util.ArrayList;
import java.util.List;public class Demo7 {public static void main(String[] args) {List<String> list = new ArrayList<>();//(8) 获取List 中的元素 / 设置 List 中的元素list.add("aaa");list.add("bbb");list.add("ccc");System.out.println(list.get(0));//aaaSystem.out.println(list.get(1));//bbbSystem.out.println(list.get(2));//ccc}
}
3.2.4 修改 List 中的元素:list.set()
import java.util.ArrayList;
import java.util.List;public class Demo8 {public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("aaa");list.add("bbb");list.add("ccc");//(9) 修改 List 中的元素list.set(0,"eee");System.out.println(list);//[eee, bbb, ccc]}
}
List 是通过 get 和 set 操作下标,为什么不让 List 像数组一样,直接用[ ] 来获取 / 修改 元素?
- 数组直接用[ ] 来获取 / 修改 元素,称为运算符重载;
- 回顾之前的方法重载:同一个类或者同一个作用域中,有多个同名的方法(只要参数不一样),就可以视为方法重载。
- C++ / Python 也支持 运算符重载,也就是针对自己定义的类,可以指定让这个类,能够支持某个运算符并且定义该运算符的行为。(此处数组的[ ] ,属于下标访问运算符)
- 但是Java 中,没有采纳这样的运算符重载。
3.2.5 遍历 List 中的元素:下标 + for each / 迭代器
import java.util.ArrayList;
import java.util.List;public class Demo9 {public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("aaa");list.add("bbb");list.add("ccc");//(10)遍历 List 中的元素,方式1:fori循环//遍历是可能会进行各种操作的,不局限于打印for (int i = 0; i < list.size(); i++) {System.out.println(list.get(i));}//遍历 List 中的元素,方式2:for each写法for (String s:list){System.out.println(s);/* aaabbbccc*/}}
}
1. 遍历 List 中的元素 方式2 的 for each写法,不是所有类都可以这样写。要求这个类,必须实现Iterable接口。
2. “迭代和迭代器”是什么?
(1)迭代:一小步一小步的,逐渐接近;使用场景:集合类中,会用到迭代。
(2)迭代器:通过这样的对象,可以实现“迭代”的效果。
3.2.6 在指定位置,添加元素:list.add(1,“111”)
import java.util.ArrayList;
import java.util.List;public class Demo10 {public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("aaa");list.add("bbb");//(11)在指定位置,添加元素//注意这里下标的含义:往指定下标之前插入元素 / 添加完毕后新元素的下标,就是这个数值/* list.add(1,"111");System.out.println(list);//[aaa, 111, bbb]*///虽然list中不存在下标为2的元素,但是此处的添加,就相当于尾插list.add(2,"111");System.out.println(list);//[aaa, bbb, 111]}
}
3.2.7 删除元素: list.remove( )
import java.util.ArrayList;
import java.util.List;public class Demo11 {public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("aaa");list.add("bbb");list.add("ccc");list.add("ddd");//(11)删除元素//a.按照下标来进行删除:删除下标为1的元素list.remove(1);System.out.println(list);//[aaa, ccc, ddd]//b.按照元素内容来进行删除list.remove("ccc");System.out.println(list);//[aaa, ddd]}
}
(1)如果list不是String类型,而是Integer类型的; remove删除是 通过 下标 删除的,而不是删除2这个元素。
(2)删除元素2的方法
3.2.8 判断元素是否存在:contains
import java.util.ArrayList;
import java.util.List;public class Demo13 {public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("aaa");list.add("bbb");list.add("ccc");list.add("ddd");//(12)通过 contains 判断元素是否存在//是通过 equals 方法来判定的,字符串内容相同,就能判定存在;而不是非得是同一个对象。System.out.println(list.contains("aaa"));//true}
}
3.2.9 查询元素的位置:indexOf / lastIndexOf
import java.util.ArrayList;
import java.util.List;public class Demo14 {public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("aaa");list.add("bbb");list.add("ccc");list.add("ddd");list.add("ccc");list.add("ccc");//(13)通过 indexOf 获取元素的位置System.out.println(list.indexOf("ccc"));//2:从前往后找,第一个ccc的下标System.out.println(list.indexOf("hhh"));//hhh不存在,则返回-1//通过 lastIndexOf 获取最后一次出现的元素的位置System.out.println(list.lastIndexOf("ccc"));//5}
}
3.2.10 获取顺序表中的 “子列表” : sublist
String 作为不可变对象,它的subString 就不存在这样的问题。
3.2.11 清空整个顺序表: clear
import java.util.ArrayList;
import java.util.List;public class Demo16 {public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("aaa");list.add("bbb");list.add("ccc");//通过 clear 清空 list 中的元素list.clear();//这里得到的是空盒子,与list = null 是有本质区别的}
}
Java 中存在“垃圾回收”机制(GC):
- 无论通过 remove 删除的元素;
- 还是通过 clear 删除的元素;
- 还是通过 list = null ;
List 中包含的所有的元素的内存空间,都能被释放掉,都是JVM手工完成的,不需要手工干预。
3.2.12 ArrayList 的扩容机制
在 ArrayList 内存空间不够的时候,自动申请额外的内存空间。
import java.util.ArrayList;
import java.util.List;public class Demo17 {public static void main(String[] args) {//关于自动扩容List<String> list = new ArrayList<>(10);for (int i = 0; i < 30; i++) {list.add("hello");}System.out.println(list);}
}
ArrayList 内部持有一个数组;设置的初始容量,相当于一个数组的大小。
new String[10] ; 就是 add的时候,把新元素从0放到9,第11次add的时候,就发现当前的元素已经满了。
(1)add真正写入元素之前,创建一个更大的数组,new String[20]。
(2)把旧数组里面的元素,拷贝到新的数组中。
(3)把新的元素添加到新数组里面。
(4)释放旧的数组。
ArrayList 扩容策略:
(1)如果调用不带参数的构造⽅法,第一次分配数组大小为10。
(2)每次扩容,容量变成之前的1.5倍。
四、 ArrayList 的具体使用
4.1 洗牌和发牌 算法
一副扑克牌 54张,去除2张大小王,剩下的52张 分为:
4种花色(黑桃、红桃、梅花、方块),13个点数(2-10+A+J+Q+K)。
(1)洗牌和发牌 代码实现:
package ArrayList;import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;//1. 这个类的对象表示 一张牌
class Card{private String suit;//花色 suitprivate String rank;//点数 rankpublic Card(String suit, String rank) {//构造方法this.suit = suit;this.rank = rank;}//suit和rank的get / set 方法public String getSuit() {return suit;}public void setSuit(String suit) {this.suit = suit;}public String getRank() {return rank;}public void setRank(String rank) {this.rank = rank;}@Overridepublic String toString() {return this.suit + this.rank ;}
}
public class Demo1 {public static void main(String[] args) {//使用 ArrayList 表示一副扑克牌ArrayList<Card> deck = creatDeck();System.out.println("原始牌组:" + deck);//洗牌/* //标准库自带的洗牌功能Collections.shuffle(deck);*/shuffle(deck);System.out.println("洗牌之后:" + deck);//4.发牌:假设有三个人,每个人发5张牌ArrayList<ArrayList<Card>> hands= new ArrayList<>();//相当于 3行5列 的二维数组//也是一个ArrayList,里面的每个元素都是一个ArrayList;//当前这个ArrayList的长度为0(空的盒子);//现在需要 往里面添加三个元素,表示3个玩家的手牌//先创建 3个人手里的牌的 ArrayListfor (int i = 0; i < 3; i++) {ArrayList<Card> hand = new ArrayList<Card>();//每个人的手牌//此时这个二维数组,已经添加了3个元素进去了;但是每个元素,还是长度为0的ArrayListhands.add(hand);}//3个人,一人发五张牌for (int i = 0; i < 5; i++) {//一人发五张牌for (int j = 0; j < 3; j++) {//发牌是轮流的过程:先取第一张牌发给第1个人;再发给第2个人;再发给第3个人ArrayList<Card> currentHand = hands.get(j);Card card = deck.remove(0);currentHand.add(card);}}//打印每个人的手牌for (int i = 0; i < 3; i++) {System.out.println("第" + i + "个人的手牌" + hands.get(i));}}//3. 自定义的洗牌操作public static void shuffle(ArrayList<Card> deck){//(1)把整个 ArrayList 从后往前遍历//(2)针对每次取到一张牌,就生成一个随机下标。拿着当前位置的牌和随机下标位置的牌,进行交换。Random random = new Random();for (int i = deck.size() - 1; i > 0 ; i--) {int j = random.nextInt(deck.size());//确保当前生成的下标,是有效的//交换两张牌的位置Card tmp = new Card(deck.get(i).getSuit(),deck.get(i).getRank());deck.set(i,deck.get(j));deck.set(j,tmp);}}public static ArrayList<Card> creatDeck(){//2. 创建一副扑克牌ArrayList<Card> deck = new ArrayList<Card>();String[] suits = {"♠","♥","♦","♣"};//花色for(String suit:suits){//在每个花色中,生成对应的点数 的牌for (int i = 2; i <= 10 ; i++) {Card card = new Card(suit,"" + i);deck.add(card);//2-10}Card cardJ = new Card(suit,"J");Card cardQ = new Card(suit,"Q");Card cardK = new Card(suit,"K");Card cardA = new Card(suit,"A");deck.add(cardJ);deck.add(cardQ);deck.add(cardK);deck.add(cardA);}return deck;}
}
(2)输出结果:
原始牌组:[♠2, ♠3, ♠4, ♠5, ♠6, ♠7, ♠8, ♠9, ♠10, ♠J, ♠Q, ♠K, ♠A, ♥2, ♥3, ♥4, ♥5, ♥6, ♥7, ♥8, ♥9, ♥10, ♥J, ♥Q, ♥K, ♥A, ♦2, ♦3, ♦4, ♦5, ♦6, ♦7, ♦8, ♦9, ♦10, ♦J, ♦Q, ♦K, ♦A, ♣2, ♣3, ♣4, ♣5, ♣6, ♣7, ♣8, ♣9, ♣10, ♣J, ♣Q, ♣K, ♣A]
洗牌之后:[♣8, ♦6, ♦7, ♥J, ♠4, ♣J, ♥A, ♦10, ♦9, ♣2, ♠3, ♥6, ♦J, ♠5, ♦2, ♠K, ♠10, ♠9, ♦K, ♦3, ♦5, ♥2, ♣6, ♥7, ♦Q, ♥Q, ♥5, ♠6, ♠7, ♦8, ♠A, ♥10, ♣9, ♥9, ♥8, ♥3, ♣4, ♠2, ♥4, ♣5, ♦A, ♠Q, ♣10, ♣3, ♣7, ♣K, ♣Q, ♠8, ♠J, ♣A, ♦4, ♥K]
4.2 杨辉三角
(1)力扣链接:LC118. 杨辉三角
(2)杨辉三角 规律图解
(3)杨辉三角 代码实现
package ArrayList;//ArrayList的具体使用2:杨辉三角import java.util.ArrayList;
import java.util.Date;
import java.util.List;public class Demo2 {public List<List<Integer>> generate (int numRows){List<List<Integer>> result = new ArrayList<List<Integer>>();//二维数组for (int i = 0; i < numRows; i++) {//构造 numRows 行List<Integer> row = new ArrayList<Integer>();//向上转型:ArrayList是具体实现的子类,List是更上层的父类(接口)for (int j = 0; j < i + 1; j++) {if(j == 0 || j == i){//杨辉三角的第一列和最后一列,都是1row.add(1);}else {//一般的列,值为:result[i-1][j-1] + result[i-1][j]int current = result.get(i-1).get(j-1) + result.get(i-1).get(j);row.add(current);}}//此处,已经把一行构造好了,只需要把这一行整体 添加到 result 中result.add(row);}return result;}public static void main(String[] args) {Demo2 demo = new Demo2();List<List<Integer>> result= demo.generate(5);System.out.println(result);}
}
输出结果:
五、 基于数组,自己实现 ArrayList
前面学习的 ArrayList,是Java中已经实现好的。那么如何基于数组,自己实现 ArrayList 呢?
package ArrayList;//自己实现一个简单的顺序表
//已知:标准库的顺序表,带有泛型参数
//此处,以 int 为例进行表示,就不写泛型了。
public class MyArrayList {//通过 arr数组 真正去存储数据,顺序表本身就是基于数组的封装。private int[] arr;//约定 arr 中前 size 个元素,表示有效元素。private int size = 0;//通过 size 标记 哪些有效,哪些无效。//此时,如果 arr 的new int[10] 是10,但是 size 仍然为0;//此时,ArrayList 也视为“空盒子”。//当后续 add 去添加元素的时候,就去变更 size:添加一个元素,size + 1;//size为几,就意味 有效元素有几个。//则[0,size) 就是整个 arr 中有效元素的部分;[size,arr.length) 就是无效元素的部分。 这是 MyarrayList 的正式代码 ///public MyArrayList(){//MyArrayList构造方法,无参数版//无参数版本,默认容量为10arr = new int[10];}public MyArrayList(int capacity) {arr = new int[capacity];//后续使用add操作,叫做添加有效元素。//而此处设定的new int[10],只是创建了这么大的空间,这些空间上的元素都是“无效”的。//这里一new,顺序表只是个“空盒子”,并非长度为10。}//(1)获取元素个数public int size(){return size;}//(2)新增元素:尾插public void add(int val){//把新的元素放到最后一个位置上,下标为 size 的位置//[0,size) 区间//如果当前的size比较小,没有达到数组的容量,直接进行上述操作就行了;// 如果数组已经满了,就得先扩容if(size == arr.length){//数组满了,需要先扩容resize();}arr[size] = val;size++;}//扩容private void resize(){//1.先创建一个更长的数组,ArrayList的容量扩展到1.5倍int[] newArr = new int[(int)(arr.length * 1.5)];//ArrayList 标准库中扩容的实现:arr.length + (arr.length >> 1)// 右移1位 相当于 乘以0.5;也就是 arr.length * 1.5//2. 把原来数组的元素复制到新数组上for (int i = 0; i < size; i++) {newArr[i] = arr[i];}//3. 用新的数组 替代 旧数组//此时,旧的数组空间 就会被垃圾回收 给释放掉(Java中由JVM自行完成)arr = newArr;}//(3)任意位置,新增public void add(int index,int val){//1.把index及其往后的元素,都给往后依次挪一步,并且是从后往前挪// 2. 具体搬多少次,取决于i的位置;i越靠前,搬运的次数就会越多,时间复杂度为O(N)//3. 尾插,不需要搬运。所以尾插的时间复杂度通常 为O(1);// 但尾插若触发扩容,时间复杂度就不是O(1);一旦扩容,也会触发搬运(搬运整个数组),时间复杂度为O(N).if(index < 0 || index > size){throw new IndexOutOfBoundsException("Index out of bounds");}//如果数组已经满了,继续添加元素,也是要先扩容if(size == arr.length){resize();}//搬运元素的操作,从后往前,依次把这个元素都往后搬运一个位置//如果元素本身的下标是 i,就把这个元素赋值到 i+1 的位置上//index 位置上的元素,也需要往后搬运一下,i >= indexfor (int i = size - 1; i >= index ; i--) {arr[i+1] = arr[i];}//此时,就相当于把 index 位置已经腾出来了。把新元素放到 index 位置上就好了arr[index] = val;//不要忘记更新sizesize++;}// 顺序表支持随机访问,get / set 的时间复杂度都是O(1) ////(4)根据下标,获取元素public int get(int index){if(index < 0 || index >= size){throw new IndexOutOfBoundsException("下标越界:" + index);}return arr[index];}//(5)根据下标,设置元素public void set(int index,int val){if(index < 0 || index >= size){throw new IndexOutOfBoundsException("下标越界:" + index);}arr[index] = val;}//(6)按照下标,删除元素(希望 remove 返回被删除的元素 )public int remove(int index){//给定下标,进行删除 ://对于尾删的情况,直接进行size--即可,不需要搬运if (index < 0 || index >= size){//在插入的时候,index 可以 == size(尾插)//而删除的时候,index == size,位置不合法throw new IndexOutOfBoundsException("下标越界:" + index);}int result = arr[index];//这个逻辑,已经涵盖到下面的一般情况了,无需单独列出
// if(index == size -1){
// //尾删
// size--;
// return result;
// }//边界值,非常容易出错!一定要带入具体的数据,分析for (int i = index; i < size -1; i++) {//带入实际值,进行分析arr[i] = arr[i+1];}//不要忘记size--;size--;return result;}//(7)按照值,删除元素public void delete(int val){//这里的delete是 remove(的重载版本/* //1. 先找到这个值 所在的位置int index = 0;for (index = 0; index < size; index++) {if(arr[index] == val){//查找过程中的比较://由于顺序表元素是int类型,所以可以直接使用 == 比较//如果是 其他类型(String 或者 对象),就需要用 equals 来比较break;//找到了}}if(index == size){//没有找到,元素不存在//不必进行删除了return;}*/int index = indexOf(val);if(index == -1){return;}//2. 然后通过上述 remove 的过程进行搬运式的删除//index 就是要删除的元素位置/* for (int i = index; i < size - 1; i++) {arr[i] = arr[i+1];}size--;*/remove(index);}//(8)判定元素是否存在public boolean contains(int val){for (int i = 0; i < size; i++) {if(arr[i] == val){return true;}}return false;}//(9)查找元素所在的位置public int indexOf(int val){for (int i = 0; i < size; i++) {if(arr[i] == val){return i;}}return -1;}public int lastIndexOf(int val){for (int i = size - 1; i >= 0;i--) {if(arr[i] == val){return i;}}return -1;}//(10)清空public void clear(){//删除所有元素,逻辑删除即可size = 0;//1. 不需要把 每个元素都设为0,/* for (int i = 0; i < size; i++) {arr[i] = 0;}*///2.不需要将数组置空。clear清空元素,清空之后,这个顺序表还要接着用//arr = null;//3.也不需要缩容 。因为一般编程过程中,内存空间不足,不是主要矛盾。//arr = new int[10];}//(11)打印操作:利用toString方法,把 MyArrayList 转化为字符串@Overridepublic String toString(){//打印格式形如:[1,2,3,4]StringBuilder stringBuilder = new StringBuilder();stringBuilder.append("[");for (int i = 0; i < size; i++) {stringBuilder.append(arr[i]);if(i < size - 1){//i == size - 1时,就是最后一个元素,最后一个元素不添加 [stringBuilder.append(",");}}stringBuilder.append("]");return stringBuilder.toString();}// 下面是测试代码 //private static void test1(){//测试尾插功能MyArrayList list = new MyArrayList();//添加16个元素,触发两次扩容list.add(1);list.add(2);list.add(3);list.add(4);list.add(1);list.add(2);list.add(3);list.add(4);list.add(1);list.add(2);list.add(3);list.add(4);list.add(1);list.add(2);list.add(3);list.add(4);System.out.println(list.size);System.out.println(list);}public static void test2(){//测试中间位置插入功能MyArrayList list = new MyArrayList(10);list.add(1);list.add(2);list.add(3);list.add(0,100);list.add(1,200);list.add(2,300);System.out.println(list);}public static void test3(){MyArrayList list = new MyArrayList(10);list.add(1);list.add(2);list.add(3);list.add(4);list.add(5);list.add(6);list.add(7);list.remove(0);System.out.println(list);list.remove(2);System.out.println(list);list.remove(4);System.out.println(list);list.remove(100);System.out.println(list);}private static void test4(){MyArrayList list = new MyArrayList(10);list.add(1);list.add(2);list.add(3);list.add(4);list.add(5);list.add(6);list.add(7);list.delete(3);System.out.println(list);list.delete(5);System.out.println(list);list.delete(100);System.out.println(list);}private static void test5(){MyArrayList list = new MyArrayList(10);list.add(1);list.add(2);list.add(3);list.add(4);list.add(5);list.add(6);list.add(7);if(list.contains(3)){System.out.println("OK");}else{System.out.println("failed");}if(list.contains(100)){System.out.println("OK");}else{System.out.println("failed");}if(list.indexOf(3) == 2){System.out.println("OK");}else{System.out.println("failed");}if(list.indexOf(100) == -1){System.out.println("OK");}else{System.out.println("failed");}if(list.lastIndexOf(5) == 4){System.out.println("OK");}else{System.out.println("failed");}if(list.lastIndexOf(100) == -1){System.out.println("OK");}else{System.out.println("failed");}/* System.out.println(list.contains(3));System.out.println(list.contains(100));System.out.println(list.indexOf(5));System.out.println(list.indexOf(100));System.out.println(list.lastIndexOf(7));System.out.println(list.lastIndexOf(100));*/list.clear(); // 直接调用,不清空System.out.println("List cleared!"); // 打印提示信息}public static void main(String[] args) {//test1();//test2();//test3();//test4();test5();}
}
【总结】
- 搬运是顺序表中核心的过程(插入 / 删除),因为数组本身不擅长中间位置的插入和删除。
- 其他的 尾插、尾删、get、set 都是比较高效的操作,时间复杂度为O(1)。
- contains、indexOf、lastIndexOf 操作,虽然时间复杂度为O(N),但代码比较简单。
- 扩容操作:
(1)需要申请新空间,拷⻉数据,释放旧空间。会有不小的消耗。
(2)⼀般是呈2倍的增⻓,势必会有⼀定的空间浪费。例如当前容量为100,满了以后增容到200,再继续插入了5个数据,后⾯没有数据插⼊了,那么就浪费了95个数据空间。
为了解决这些问题,引入了“链表”这样的结构;虽然链表能解决这些问题,但也引入了不少问题。
因此,实际开发中,还是以 顺序表 使用的情况更多。
相关文章:
【数据结构】线性表( List)和 顺序表(ArrayList)
【数据结构】线性表( List)和 顺序表(ArrayList) 一、线性表 List二、List 接口的常用方法三、ArrayList与顺序表3.1 引入顺序表的原因?3.2 ArrayList 的使用3.2.1 ArrayList 的创建3.2.2 添加元素:list.ad…...
嵌入式开发--STM32软件和硬件CRC的使用--续篇
本文是《嵌入式开发–STM32软件和硬件CRC的使用》的续篇,又踩到一个坑,发出来让大家避一下坑。 按照G0系列的设置,得出错误的结果 前文对应的是STM32G0系列,今天在用STM32G4系列时,按照前文的设置,用硬件…...
探索鸡养殖虚拟仿真实验:科技赋能养殖新体验
在科技飞速发展的今天,虚拟仿真技术逐渐渗透到各个领域,就连传统的养殖业也迎来了数字化的变革。最近,我参与了一场别开生面的鸡养殖虚拟仿真实验,不仅学到了专业的养殖知识,还收获了前所未有的沉浸式体验。现在&#…...
知识图谱中医知识问答系统|养生医案综合可视化系|推荐算法|vue+flask+neo4j+mysql
文章结尾部分有CSDN官方提供的学长 联系方式名片 文章结尾部分有CSDN官方提供的学长 联系方式名片 关注B站,有好处! ✅编号 :F040 pro ✅技术架构: vueflaskmysqlneo4jltpac ✅实现功能:实现基于中医药材和药方的知识图谱可视化,在…...
【AI】——结合Ollama、Open WebUI和Docker本地部署可视化AI大语言模型
🎼个人主页:【Y小夜】 😎作者简介:一位双非学校的大三学生,编程爱好者, 专注于基础和实战分享,欢迎私信咨询! 🎆入门专栏:🎇【MySQL࿰…...
AI 模型高效化:推理加速与训练优化的技术原理与理论解析
AI 模型高效化:推理加速与训练优化的技术原理与理论解析 文章目录 AI 模型高效化:推理加速与训练优化的技术原理与理论解析一、推理加速:让模型跑得更快的“程序员魔法”(一)动态结构自适应推理:像人类一样…...
python学习—详解word邮件合并
系列文章目录 python学习—合并TXT文本文件 python学习—统计嵌套文件夹内的文件数量并建立索引表格 python学习—查找指定目录下的指定类型文件 python学习—年会不能停,游戏抽签抽奖 python学习—循环语句-控制流 python学习—合并多个Excel工作簿表格文件 pytho…...
vscode与vim+cscope+tags热键冲突
[ctrl w] s 对于vim时水平分割窗口热键 对vscode, [ctrl w]时关闭当前窗口热键 在vscode中如下配置可以发送热键到shell, 跳过vscode:...
直播系统源码开发:解锁幸运礼物功能的商业魔力与运营策略
在当今如火如荼的直播经济中,幸运礼物功能已成为平台提升用户黏性、刺激消费的"黄金按钮"。山东布谷科技将深入剖析幸运礼物功能的技术逻辑与商业价值,并为运营者提供一套完整的策略框架,帮助您在激烈的直播赛道中脱颖而出。 一、…...
毕业设计效率提升工具与避坑指南
本文为毕业设计后的经验记录,包含写作过程中的一些实用工具和注意事项。 一、📌实验及写作实用技巧二、🚀 效率提升工具三、📊论文完成后的格式检查 本文为毕业设计后的经验记录,包含写作过程中的一些实用工具和注意事…...
Python网络爬虫设计(二)
目录 六、BeautifulSoup库 1、常见的提取分析网页内容的三种方式 (1)正则表达式 (2)BeautifulSoup库 (3)pyppeteer库中的元素查找函数 2、HTML中的tag 3、BeautifulSoup库的安装和导入 4、Beautiful…...
滑动窗口209. 长度最小的子数组
1.题目 给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组中满足其总和大于等于 target 的长度最小的 子数组 [numsl, numsl1, ..., numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。 示例 1: 输入&…...
如何避免被目标网站识别为爬虫?
文章目录 前言1. 合理设置请求头2. 控制请求频率3. 模拟真实用户行为4. 使用代理 IP5. 处理验证码6. 会话管理 前言 为避免被目标网站识别为爬虫,可从请求头设置、请求频率控制、模拟用户行为、使用代理、处理验证码和会话管理等多个方面采取措施,以下是…...
Dell戴尔服务器 PowerEdge R750xs + window server2012r2 || 2016
因要求需要给新服务器装个 win server2012或者2016系统 XXX使用U盘制作PE系统U盘安装系统不行,适合普通win8,win10,win11U盘制作PE系统U盘安装win10系统教程U盘制作PE系统U盘安装win10系统教程https://mp.weixin.qq.com/s/t0W8aNJaHPAU8T78nh…...
如何通过数据分析提升软件开发项目的成功率?
引言 在软件开发中,项目延期、超预算、需求反复变更等问题屡见不鲜。数据分析作为项目管理的重要工具,正在被越来越多的企业用于提升项目成功率。通过科学利用项目数据,团队可以做出更准确的决策,避免重复踩坑,从而大幅…...
模型的RAG
RAG 什么是RAG 当岳不群相当武林的盟主时候,你的给他一个葵花宝典(秘籍RAG) RAG的原理 建立索引: 首先要清洗和提取原始数据,将 PDF、Docx等不同格式的文件解析为纯文本数据 然后将文本数据分割成更小的片段(chunk)…...
基于多模态双路TCN-SE-YOLO的小目标检测
首先声明:该思路在小目标检测领域尚未有成果发表,感兴趣的小伙伴可以借鉴! 一、引言 1.1 研究背景 小目标检测在交通监控(车牌识别)、工业检测(PCB缺陷)及农业(病虫害斑点)等领域具有重要应用价值传统单模态检测方法在复杂场景下的漏检率高达40%以上(VisDrone 2021…...
idea maven 命令后控制台乱码
首先在idea中查看maven的编码方式 执行mvn -v命令 查看编码语言是GBK C:\Users\13488>mvn -v Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f) Maven home: D:\maven\apache-maven-3.6.3\bin\.. Java version: 1.8.0_202, vendor: Oracle Corporation, runt…...
在Vmware15(虚拟机免费) 中安装纯净win10详细过程
一、软件备选 1. VMware15.5.1 网盘下载地址 链接: https://pan.baidu.com/s/1y6GLJ2MG-1tomWblt3otsg?pwdim8e 提取码: im8e 2. windows镜像下载 去官网下载ios包 链接:https://www.microsoft.com/zh-cn/software-download/windows10 二、在VMware15.5.1下安装w…...
RISC-V 与 OpenHarmony 的结合意义与应用建议
RISC-V 与 OpenHarmony 的结合意义与应用建议 一、结合的意义 (一)硬件与软件的协同创新 RISC-V 作为硬件层的开源指令集架构,为 OpenHarmony 提供了强大的硬件支持。这种支持不仅体现在硬件性能的提升上,还为 OpenHarmony 的分…...
让SQL飞起来:搭建企业AI应用的SQL性能优化实战
我上一篇文章已经讲解过了如何使用公开的AI模型来优化SQL.但这个优化方法存在一定的局限性.因为公开的AI模型并不了解你的数据表结构是什么从而导致提供的优化建议不太准确.而sql表结构又是至关重要的安全问题,是不能泄露出去的.所以在此背景下我决定搭建一个自己的AI应用在内网…...
驱动开发硬核特训 · Day 14:深入理解 Power 管理驱动架构与实战应用
在嵌入式系统中,Power(电源)管理驱动既关乎系统稳定性,又直接影响功耗与续航,是系统设计中绕不开的核心模块。今天我们通过理论实战的形式,一次性讲清楚: Linux 中电源管理驱动的核心框架Regul…...
备份思科路由器设备文件实例
实例需求: (1)备份路由器的配置文件startup-config和映像文件 (2)备份交换机的配置文件startup-config和映像文件 注:PC3为TFTP服务器 结构示意图: 实例配置一: 备份路由器的配置文件startup-config和映像文件 步骤: 在PC3上打开tftp服务。确保PC3可以ping通11.1.1.…...
游戏引擎学习第231天
设定当天的主题 我们现在到了一个很少出现在直播中的阶段,但今天是那种需要解释计算机科学基础概念的日子。因此,今天我们将讨论这个内容,今天的重点是“大O表示法”(Order Notation),我将用黑板来解释这些…...
PclSharp ——pcl的c#nuget包
简介: NuGet Gallery | PclSharp 1.8.1.20180820-beta07 下载.NET Framework 4.5.2 Developer Pack: 下载 .NET Framework 4.5.2 Developer Pack Offline Installer 离线安装nupkg: nupkg是visual studio 的NuGet Package的一个包文件 安…...
Java性能剖析工具箱
1. 基础知识 1.1 Java性能调优概述 1.1.1 性能调优的重要性 性能调优是提升系统效率、降低成本和增强用户体验的关键步骤。通过优化,可以减少响应时间、降低资源消耗并提高系统的稳定性和可扩展性。 1.1.2 性能问题的常见表现 高CPU使用率:可能由热点方法或线程阻塞引起。…...
信息学奥赛一本通 1622:Goldbach’s Conjecture | 洛谷 UVA543 Goldbach‘s Conjecture
【题目链接】 ybt 1622:Goldbach’s Conjecture 洛谷 UVA543 Goldbach’s Conjecture 【题目考点】 1. 筛法求质数表 埃筛线性筛(欧拉筛) 知识点讲解见信息学奥赛一本通 2040:【例5.7】筛选法找质数 【解题思路】 首先使用埃…...
408数据结构绪论刷题001
答案:D 解析: • A选项:数据元素是组成数据对象的基本单位 ,它只是数据的基本个体,不能完整定义数据结构,所以A选项错误。 • B选项:数据对象是性质相同的数据元素的集合,仅仅描述…...
RNN - 语言模型
语言模型 给定文本序列 x 1 , … , x T x_1, \ldots, x_T x1,…,xT,语言模型的目标是估计联合概率 p ( x 1 , … , x T ) p(x_1, \ldots, x_T) p(x1,…,xT)它的应用包括 做预训练模型(eg BERT,GPT-3)生成本文ÿ…...
前端面试题---GET跟POST的区别(Ajax)
GET 和 POST 是两种 HTTP 请求方式,它们在传输数据的方式和所需空间上有一些重要区别: ✅ 一句话概括: GET 数据放在 URL 中,受限较多;POST 数据放在请求体中,空间更大更安全。 📦 1. 所需空间…...
【MCP】第一篇:MCP协议深度解析——大模型时代的“神经连接层“架构揭秘
【MCP】第一篇:MCP协议深度解析——大模型时代的"神经连接层"架构揭秘 一、什么是MCP?二、为什么需要MCP?三、MCP的架构四、MCP与AI交互的原理4.1 ReAct(Reasoning Acting)模式4.2 Function Calling 模式 五…...
新生宿舍管理系统
收藏关注不迷路!! 🌟文末获取源码数据库🌟 感兴趣的可以先收藏起来,还有大家在毕设选题(免费咨询指导选题),项目以及论文编写等相关问题都可以给我留言咨询,希望帮助更多…...
@Autowird 注解与存在多个相同类型对象的解方案
现有一个 Student 类,里面有两个属性,分别为 name 和 id;有一个 StuService 类,里面有两个方法,返回值均为类型为 Student 的对象;还有一个 StuController 类,里面有一个 Student 类型的属性&am…...
MQTT客户端核心架构解析:clients.h源码深度解读
MQTT客户端核心架构解析:clients.h源码深度解读 一、头文件概览与设计哲学 clients.h作为MQTT客户端核心数据结构定义文件,体现了以下设计原则: 分层架构:网络层/协议层/业务层解耦状态管理:通过状态机实现复杂协议…...
音视频学习 - ffmpeg 编译与调试
编译 环境 macOS Ventrua 13.4 ffmpeg 7.7.1 Visual Studio Code Version: 1.99.0 (Universal) 操作 FFmpeg 下载源码 $ cd ffmpeg-x.y.z $ ./configure nasm/yasm not found or too old. Use --disable-x86asm for a crippled build.If you think configure made a mistake…...
解读《人工智能指数报告 2025》:洞察 AI 发展新态势
美国斯坦福大学 “以人为本人工智能研究院”(HAI)近日发布的第八版《人工智能指数报告》(AI Index Report 2025)备受全球瞩目。自 2017 年首次发布以来,该报告一直为政策制定者、研究人员、企业高管和公众提供准确、严…...
【嵌入式系统设计师(软考中级)】第一章:计算机系统基础知识(中)
文章目录 3 算术运算和逻辑运算3.1 二进制数运算方法3.2 逻辑代数的基本运算与逻辑表达式化简 4. 计算机组成及工作原理4.1 CPU的组成与工作原理4.1.1 运算器(数据加工中心)4.1.2 控制器(指令指挥中心)4.1.3 计算机指令4.1.4 寻址…...
实时数据处理的革命:Apache Flink 在大数据流处理中的应用
实时数据处理的革命:Apache Flink 在大数据流处理中的应用 在大数据时代,数据的价值不仅仅体现在存储和分析,更重要的是实时处理。传统的批处理模式往往无法满足现代业务对数据的实时性需求,而流式计算技术的兴起,让数据处理从“静态分析”变成了“动态决策”。其中,Apa…...
HttpSessionListener 的用法笔记250417
HttpSessionListener 的用法笔记250417 以下是关于 HttpSessionListener 的用法详解,涵盖核心方法、实现步骤、典型应用场景及注意事项,帮助您全面掌握会话(Session)生命周期的监听与管理: 1. 核心功能 HttpSessionLi…...
基于html实现的课题随机点名
这是一个用于随机点名系统的HTML网页,具有中国古典风格的设计。 下面我将从多个方面详细介绍这个文件: 1. 文件基本信息 文件名:name.html 文件类型:HTML5文档 语言:简体中文(zh-CN) 编码:UTF-8 标题&…...
【KWDB 创作者计划】深度实操体验 KWDB 2.2.0:从安装到实战的全流程解析以及实操体验
一、引言 KWDB 是一款高性能的分布式数据库,支持事务、强一致性和水平扩展。本文将详细介绍如何通过 Docker 快速部署 KWDB 2.2.0,并基于实际操作演示数据库的核心功能,涵盖环境准备、容器运行、数据操作及集群部署等关键环节。 二、Docker…...
ASP.NET Core中SqlSugar基本使用
创建数据模型 public class News{[SugarColumn(IsIdentity true, IsPrimaryKey true)]public int Id { get; set; }//nvarchar带中文比较好[SugarColumn(ColumnDataType "nvarchar(30)")]public string Title { get; set; }[SugarColumn(ColumnDataType "te…...
【软考-系统架构设计师】设计模式三大类型解析
设计模式三大类型深度解析 一、创建型模式(Creational Patterns) 核心目标:解耦对象的创建与使用过程,提供灵活的对象生成机制,降低系统对具体类的依赖。 适用场景:需要动态创建对象、隐藏对象创建细节或…...
正则表达式在爬虫中的应用:匹配 HTML 和 JSON 的技巧
在爬虫开发中,正则表达式是一种强大的工具,可以帮助我们从复杂的文本中提取所需信息。无论是处理 HTML 页面还是 JSON 数据,正则表达式都能发挥重要作用。本文将深入探讨正则表达式在爬虫中的应用,包括如何匹配 HTML 和 JSON 数据…...
LaTeX文章写法
文章目录 模板1、无序列表格式2、对齐2.1、section对齐 模板 文章模板 %\documentclass[a4paper,12pt]{article} % 选择 A4 纸张和 12pt 字体大小 \documentclass[12pt,a4paper]{ctexart}% 加载必要的宏包 \usepackage{fontspec} % 支持字体设置 \usepackage{xeCJK} …...
电力变压器油的<油质气象色谱>指标分析
目录 1.变压器油质化验指标分析 2.变压器油质化验原理及流程 变压器油质气象色谱(气相色谱,Gas Chromatography, GC)检测是一种通过分离和定量分析油中溶解气体成分的技术,用于诊断变压器内部故障。其核心原理基于不同气体在流动…...
赋能能源 | 智慧数据,构建更高效智能的储能管理系统
行业背景 随着新能源产业的快速发展,大规模储能系统在电力调峰、调频及可再生能源消纳等领域的重要性日益凸显。 储能电站作为核心基础设施,其能量管理系统(EMS)需要处理海量实时数据,包括电池状态、功率变化、环境监…...
AWS中国区服务部署与ICP备案全流程指南:从0到1实现合规上线
导语: 在中国大陆地区使用AWS服务,不仅需要了解AWS的基本操作,还需要熟悉中国特有的法规要求。本文将为您提供一个全面的指南,涵盖AWS中国区账号创建、服务部署、ICP备案申请,以及合规运营的全过程。无论您是AWS新手还是经验丰富的开发者,这篇文章都将为您在AWS中国区的journey…...
android系统使用FFmpeng集成OpenSL音频录制和播放
目录 一、背景 二、方案 三、代码实现 3.1 初始化OpenSL 3.2 设置播放回掉 3.3 使用FFmpeg计算出转换后的样本数目 一、背景 FFmpeg不能够操作Android的硬件设备,所以要在Android系统上面播放音频的话需要另找办法 二、方案 Android 环境下音频播放通常有两…...
顺序表和链表,时间和空间复杂度--数据结构初阶(1)(C/C++)
文章目录 前言时间复杂度和空间复杂度理论部分习题部分 顺序表和链表理论部分作业部分 前言 这期的话会给大家讲解复杂度,顺序表和链表的一些知识和习题部分(重点是习题部分,因为这几个理念都比较简单) 时间复杂度和空间复杂度 理论部分 时间复杂度和…...