当前位置: 首页 > news >正文

篇章四 数据结构——顺序表

目录

1.List

1.1 什么是 List

1.2 常见接口介绍

1.3 List的使用

2. ArrayList 与 顺序表

2.1 线性表

2.2 顺序表

1.什么是顺序表

2.为什么要有顺序表?

3.自己实现一个add方法

2.3 ArrayList简介

2.4 ArrayList使用

1. ArrayList的构造

1.1 ArrayList 基础属性的含义​编辑

1.2 ArrayList() 解析

1.3 ArrayList(int initialCapacity) 解析

1.4 ArrayList(Collection c) 解析

2. ArrayList 的常见操作

2.1 add(int index, E element)

​编辑

2.2 remove的区分

2.3 subList 存在问题

3. ArrayList的遍历

4. ArrayList的扩容

5.ArrayList的练习

5.1 用于字符串

5.2 杨辉三角:

5.3 洗牌算法

2.5. ArrayList的缺点


1.List

1.1 什么是 List

在 集合框架 中,List 是一个接口,继承自 Collection

Collection 也是一个接口,该接口 规范 了后序容器中常用的一些方法, 具体如下:

Iterable 也是一个接口,表示实现该接口的类是可以 逐个元素遍历 的,具体如下:

List的官方文档

但是站在 数据结构 的角度来看,List 就是一个 线性表

即:n 个具有 相同类型元素 的 有限序列

在该序列上可以执行 增删改查 以及 遍历 等操作。

1.2 常见接口介绍

List中提供了好多的方法,具体如下:

虽然方法比较多,但是常用方法如下:

增:add addAll

删:remove clear

改:set 

查:get indexOf lastIndexOf

裁:subList

1.3 List的使用

注意:List是个接口,并不能直接用来实例化。

如果要使用,必须去实例化List的实现类。在集合框中,ArrayList 和 LinkedList 都实现了List接口。

2. ArrayList 与 顺序表

2.1 线性表

线性表 (linear list)是 n 个具有 相同特性 的数据元素的有序序列。

常见的线性表:顺序表、链表、栈、队列 ......

线性表在 逻辑 上是线性结构,也就说是 连续的一条直线 。但是在 物理结构 上并不一定是连续的,线性表在物理上存储时,通常以 数组和链式结构 的形式存储

2.2 顺序表

1.什么是顺序表

顺序表是用一段 物理地址连续的存储单元 依次存储数据元素的线性结构

2.为什么要有顺序表?

数组本身自带的方法无法满足需求。

需要我们自己定义一个类,让类来提供方法,帮助操作数组。

3.自己实现一个add方法

画图:

代码:

public void add(int data) {if (isFull()) {grow();}this.array[this.usedSize] = data;this.usedSize++;}private void grow() {this.array = Arrays.copyOf(this.array, 2*this.array.length)}public boolean isFull() {return this.usedSize == array.length;}

逻辑非常严谨。

所以感觉很简单的功能,要写很多代码来保证严谨性。

2.3 ArrayList简介

在集合框架中,ArrayList是一个普通的类,实现了List接口,具体框架图如下:

【说明】

泛型:

ArrayList是以泛型方式实现的,使用时必须要先实例化

接口:

ArrayList实现了RandomAccess接口,表明ArrayList支持随机访问

ArrayList实现了Cloneable接口,表明ArrayList是可以clone的

ArrayList实现了Serializable接口,表明ArrayList是支持序列化的

并发:

和Vector不同,ArrayList不是线程安全的,在单线程下可以使用,在多线程中可以选择Vector或者CopyOnWriteArrayList

底层:

ArrayList底层是一段连续的空间,并且可以动态扩容,是一个动态类型的顺序表

2.4 ArrayList使用

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 构造 list3List<Integer> list3 = new ArrayList<>(list2);}
1.1 ArrayList 基础属性的含义
1.2 ArrayList() 解析

此处涉及到一部分扩容,仔细理解,扩容剩余部分在扩容部分进行讲解.

1.3 ArrayList(int initialCapacity) 解析

1.4 ArrayList(Collection<? extends E> c) 解析

2. ArrayList 的常见操作

2.1 add(int index, E element)
2.2 remove的区分

此处红线原因:

        因为这个操作过时了,但是可以用就是用的很少。

2.3 subList 存在问题

public static void main(String[] args) {ArrayList<Integer> test = new ArrayList<>();test.add(1);test.add(2);test.add(3);test.add(4);test.add(5);System.out.println(test);List<Integer> list = test.subList(1, 3);System.out.println(list);
}

[1,3)

输出结果:

此时对裁剪后的数组进行改变,会出现与预期不符合的效果.

    public static void main3(String[] args) {ArrayList<Integer> test = new ArrayList<>();test.add(1);test.add(2);test.add(3);test.add(4);test.add(5);System.out.println(test);List<Integer> list = test.subList(1, 3);System.out.println(list);System.out.println("======================================");list.set(0, 99);System.out.println(test); // 预期 1 2 3 4 5System.out.println(list); // 预期 99 3}

结果:

原因(如下图表示和代码分析):直接引用并没有创建新的对象

3. ArrayList的遍历

下面代码介绍了 ArrayList 的四种遍历方式

public static void main(String[] args) {ArrayList<Integer> test = new ArrayList<>();test.add(1);test.add(2);test.add(3);test.add(4);test.add(5);System.out.println(test);System.out.println("===使用迭代器输出 listIterator 拓展功能===");ListIterator<Integer> iterator3 = test.listIterator(test.size());while (iterator3.hasPrevious()) {System.out.print(iterator3.previous() + " ");}System.out.println();System.out.println("===使用迭代器输出 iterator===");Iterator<Integer> iterator = test.iterator();while (iterator.hasNext()) {System.out.print(iterator.next() + " ");}System.out.println();System.out.println("===使用迭代器输出 listIterator===");ListIterator<Integer> iterator2 = test.listIterator();while (iterator2.hasNext()) {System.out.print(iterator2.next() + " ");}System.out.println();int size = test.size();System.out.println("===for循环输出===");for (int i = 0; i < size; i++) {System.out.print(test.get(i) + " ");}System.out.println();System.out.println("===for each循环输出===");for (Integer x : test) {System.out.print(x + " ");}System.out.println();
}

4. ArrayList的扩容

总结:

  1. 检测是否真正需要扩容,如果是调用grow准备扩容

  2. 预估需要库容的大小初步预估按照1.5倍大小扩容如果用户所需大小超过预估1.5倍大小,则按照用户所需大小扩容,真正扩容之前检测是否能扩容成功,防止太大导致扩容失败

  3. 使用copyOf进行扩容

具体解析如下图:

5.ArrayList的练习

5.1 用于字符串

代码:

为什么 str2.contains(ch + "") 要 + ""   

因为contains的参数是 CharSequence 而不是 Character

5.2 杨辉三角:

public List<List<Integer>> generate(int numRows) {List<List<Integer>> ret = new ArrayList<>();​    List<Integer> list0 = new ArrayList<>();list0.add(1);ret.add(list0);​    // 从第二行开始 进行求每个元素for (int i = 1; i < numRows; i++) {// 处理第一个元素List<Integer> curRow = new ArrayList<>();curRow.add(1);​        // 中间List<Integer> preRow = new ArrayList<>();preRow = ret.get(i - 1);for (int j = 1; j < i; j++) {int val1 = preRow.get(j);int val2 = preRow.get(j - 1);curRow.add(val1 + val2);}​        // 尾巴curRow.add(1);ret.add(curRow);
}
5.3 洗牌算法

买牌:

public List<Cart> buyCard() {List<Cart> cartList = new ArrayList<>();for (int i = 1; i <= 13; i++) {for (int j = 0; j < 4; j++) {int rank = i;String suit = suits[j];Cart cart = new Cart(suit, rank);cartList.add(cart);}}return cartList;
}

洗牌:

public void shuffle(List<Cart> cartList) {Random random = new Random();for (int i = cartList.size() - 1; i > 0; i--) {int index = random.nextInt(cartList.size());swap(cartList, i, index);}
}private void swap(List<Cart> cartList, int i, int j) {Cart tmp = cartList.get(i);cartList.set(i, cartList.get(j));cartList.set(j, tmp);
}

 玩牌:

public List<List<Cart>> play(List<Cart> cartList) {List<Cart> hand0 = new ArrayList<>();List<Cart> hand1 = new ArrayList<>();List<Cart> hand2 = new ArrayList<>();​    List<List<Cart>> hand = new ArrayList<>();hand.add(hand0);hand.add(hand1);hand.add(hand2);​    for (int i = 0; i < 5; i++) {for (int j = 0; j < 3; j++) {Cart cart = cartList.remove(0);hand.get(j).add(cart);}}​    return hand;
}

2.5. ArrayList的缺点

  1. ArrayList底层使用连续的空间,任意位置插入或删除元素时,需要将该位置后序元素整体往前或者往后搬移,故时间复杂度为O(N)

  2. 扩容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。

  3. 扩容一般是呈 1.5 倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。

 

相关文章:

  • 52、C# 泛型 (Generics)
  • *HTML `<script>` 标签中的核心属性解析:掌控脚本加载与执行的艺术
  • HTML 文件路径完全指南:相对路径、绝对路径解析与引用技巧
  • 计算机网络-MPLS VPN应用场景与组网
  • React从基础入门到高级实战:React 核心技术 - 表单处理与验证深度指南
  • YOLOv12增加map75指标
  • 深度学习---可视化
  • 华为云Flexus+DeepSeek征文 | Dify-LLM平台一键部署教程及问题解决指南
  • 项目部署一次记录
  • 通过chrome插件自动生成博客评论,高效发外链
  • P5734 【深基6.例6】文字处理软件
  • LVGL(lv_animimg)
  • 当前上下文中不存在名称“X509CertificateLoader”,编译.NET 9依赖
  • 数据库入门教程:以商品订单系统为例
  • LeetCode百题刷004(哈希表优化两数和问题)
  • NGINX 用户标识模块 (ngx_http_userid_module) 完整配置与最佳实践指南
  • 图论:floyed算法
  • java枚举和mybaits-plus结合实现映射输出和存储
  • 图论 判断是否有环
  • sqli-labs第二十八关——Trick with ‘union select‘
  • 在线做头像网站/代发关键词排名包收录
  • 富阳设计网站/短视频营销推广方案
  • 淡水网站建设公司/windows优化大师可以卸载吗
  • 建设厅网站/googleseo优化
  • 余姚网站建设公司/seo型网站
  • 碳晶板装修多少钱一平方/南京seo排名扣费