java数据结构集合复习之ArrayList与顺序表

前言: 这是我最一年学习java的一部分的回顾总结

1.List

1.1什么是List?

在框架集合中,List是一个接口,继承自Collection。
在这里插入图片描述

Collection也是一个接口,该接口中规范了后序容器中常用的一些方法,具体如下所示
在这里插入图片描述
在这里插入图片描述

--------
boolean add(E e)尾插 e
void add(int index, E element)将 e 插入到 index 位置
boolean addAll(Collection<? extends E> c)尾插 c 中的元素
E remove(int index)删除 index 位置元素
boolean remove(Object o)删除遇到的第一个 o
E get(int index)获取下标 index 位置元素
E set(int index, E element)将下标 index 位置元素设置为 element
void clear()清空
boolean contains(Object o)判断 o 是否在线性表中
int indexOf(Object o)返回第一个 o 所在下标
int lastIndexOf(Object o)返回最后一个 o 的下标
List subList(int fromIndex, int toIndex)截取部分 list

站在数据结构的角度来看,List就是一个线性表,即n个具有相同类型元素的有限序列,在该序列上可以执行增删改查以及变量等操作。

1.2 List的使用

List是个接口,并不能直接用来实例化
如果要使用,必须去实例化List的实现类

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;public class ListExample {public static void main(String[] args) {// 使用 ArrayList 实现类List<String> arrayList = new ArrayList<>();arrayList.add("Apple");arrayList.add("Banana");arrayList.add("Orange");System.out.println("ArrayList: " + arrayList);// 使用 LinkedList 实现类List<String> linkedList = new LinkedList<>();linkedList.add("Mango");linkedList.add("Kiwi");linkedList.add("Grape");System.out.println("LinkedList: " + linkedList);}
}

List 不能直接实例化是因为接口本身只是一种规范或契约,它定义了一组方法的签名,但并没有提供这些方法的具体实现。
接口的主要目的是为了实现多态性和代码的解耦。通过定义接口,不同的类可以实现相同的接口,从而以统一的方式进行处理。 打个比方,想象 List
接口是一个菜谱,它只规定了要有哪些菜(方法),但没有告诉你具体怎么做这些菜(方法的实现)。只有具体的厨师(实现类),比如 ArrayList
或者 LinkedList ,才能按照这个菜谱做出实际的菜肴(实现方法)。

ArrayList与顺序表

2.1 线性表

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

在这里插入图片描述

2.2 顺序表

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

下面是手动实现一个顺序表的实现

package com;import java.util.Arrays;public class SeqList {private int[] array;//记录当前顺序表当中 有多少个有效的数据private int size;private static final int INIT_CAPACITY = 5;// 默认构造方法 将顺序表的底层容量设置为INIT_CAPACITYpublic SeqList(){this.array = new int[INIT_CAPACITY];}//判断当前顺序表是否满了 注意在进行新增操作是都要考虑数组是否需要判断满表public boolean isFull(){//返回当前表中的元素个数与当前表的长度作比较若相等是ture,反之falsereturn size == array.length;}//给数组扩容 注意在进行新增操作是都要考虑数组是否需要扩容private void resize(){array = Arrays.copyOf(array,2*array.length);}// 新增元素,默认在数组最后新增public void add(int data){if (isFull()){resize();}this.array[size] = data;//将当前指针位置+1,每次新增操作都需要size++;}// 在 pos 位置新增元素public void add(int pos,int data){//判断pos位置合不合法if (pos<0 || pos>this.size){throw new PosOutBoundsException("add 元素的时候,pos位置不合法!");}if(isFull()){resize();}for (int i = size-1; i >= pos; i--) {array[i+1] = array[i];}array[pos] = data;size++;}// 判定是否包含某个元素public boolean contains(int toFind) {for (int i = 0; i <this.size; i++) {if(array[i] == toFind){return true;}}return false;}// 查找某个元素对应的位置public int indexOf(int toFind) {for (int i = 0; i < this.size; i++) {if (array[i] == toFind){return i;}}return -1;}// 获取 pos 位置的元素public int get(int pos) {if (pos<0 || pos>this.size){throw new PosOutBoundsException("pos位置不合法!");}return array[pos];}// 给 pos 位置的元素设为 valuepublic void set(int pos, int value) {if (pos<0 || pos>this.size){throw new PosOutBoundsException("pos位置不合法!");}this.array[pos] = value;}//删除第一次出现的关键字keypublic void remove(int toRemove) {if (isEmpty()){return;}int index = indexOf(toRemove);if (index == -1){System.out.println("没有你要删除的数据");}for (int i = index; i < this.size-1; i++) {this.array[i] = this.array[i+1];}size--;}// 获取顺序表长度public int size() {return size;}// 清空顺序表public void clear() {size=0;}// 打印顺序表,注意:该方法并不是顺序表中的方法,为了方便看测试结果给出的public void display() {for (int i = 0; i < this.size; i++) {System.out.println(this.array[i]+ " ");}}public boolean isEmpty(){return this.size == 0;}public static void main(String[] args) {SeqList seqList = new SeqList();seqList.add(1);seqList.add(2);seqList.add(3);seqList.add(4);seqList.add(1,10000);seqList.display();}
}

2.3 ArrayList的遍历

ArrayList 可以使用三方方式遍历:for循环+下标、foreach、使用迭代器

public static void main(String[] args) {List<Integer> list = new ArrayList<>();list.add(1);list.add(2);list.add(3);list.add(4);list.add(5);// 使用下标+for遍历for (int i = 0; i < list.size(); i++) {System.out.print(list.get(i) + " ");}System.out.println();// 借助foreach遍历for (Integer integer : list) {System.out.print(integer + " ");}System.out.println();Iterator<Integer> it = list.listIterator();while (it.hasNext()){System.out.print(it.next()+" ");}System.out.println();}

在这里插入图片描述
注意:

  1. ArrayList最常使用的遍历方式是:for循环+下标 以及 foreach
  2. 迭代器是设计模式的一种

2.4ArrayList的扩容机制

ArrayList是一个动态类型的顺序表,即:在插入元素的过程中会自动扩容。以下是ArrayList源码中扩容方式:

private static final int DEFAULT_CAPACITY = 10;// 默认容量大小
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};// 默认空间
transient Object[] elementData; 存放元素的空间public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
// 获取旧空间大小
int oldCapacity = elementData.length;
// 预计按照1.5倍方式扩容
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 如果用户需要扩容大小 超过 原空间1.5倍,按照用户所需大小扩容
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 如果需要扩容大小超过MAX_ARRAY_SIZE,重新计算容量大小
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 调用copyOf扩容
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
// 如果minCapacity小于0,抛出OutOfMemoryError异常
if (minCapacity < 0)
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}

总结:

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

2.5 ArrayList的小练习

给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。
杨辉三角
解法:

 public List<List<Integer>> generate(int numRows) {List<List<Integer>> allList = new ArrayList<>();for (int i = 0; i < numRows; i++) {List<Integer> list = new ArrayList<>();list.add(1);for (int j = 1; j < i; j++) {list.add(allList.get(i-1).get(j-1)+allList.get(i-1).get(j));}if(i != 0){list.add(1);}allList.add(list);}return allList;}

2.6ArrayList的问题及思考

问题:

  1. ArrayList底层使用连续的空间,任意位置插入或删除元素时,需要将该位置后序元素整体往前或者往后搬移,故时间复杂度为O(N)
  2. 增容需要申请新空间,拷贝数据释放旧空间,会有不小的消耗
  3. 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。

思考:
如何解决以上问题呢?

  1. 对于频繁的插入或删除元素 我们可以适合的数据结构,例如LinkedList。LinkedList底层使用链表实现,在链表中间进行插入和删除操作的时间复杂度为 O(1),但它在随机访问元素时的性能相对较差。

  2. 针对增容带来消耗的问题:
    如能预先估计集合可能需要存储的元素数量,在创建ArrayList时指定合适的初始容量,可以减少扩容的次数。或者采用内存池技术:创建一个内存池来管理内存分配和释放。当需要扩容时,从内存池中获取预先分配好的合适大小的内存块,而不是每次都进行新的内存申请和释放操作。

  3. 关于增容导致的空间浪费问题:
    一种解决思路是使用自定义的动态数组实现,根据实际元素数量更精确地控制扩容策略,而非简单地按照固定倍数扩容。例如,可以根据当前元素数量和一个预设的负载因子来决定是否扩容以及扩容的幅度。但这种方式需要自己实现动态数组的相关逻辑,增加了编程的复杂性

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

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

相关文章

[pwn]静态编译

静态编译 1. 栈足够大的情况下 程序在ida打开后&#xff0c;左侧的函数栏目没有红色&#xff08;系统调用的函数&#xff09;&#xff0c;而只有一些静态函数&#xff0c;通常这类文件的大小会必普通的pwn题程序要大得多。 这种静态编译的题没有调用库函数&#xff0c;也就没…

百度云智能媒体内容分析一体机(MCA)建设

导读 &#xff1a;本文主要介绍了百度智能云MCA产品的概念和应用。 媒体信息海量且复杂&#xff0c;采用人工的方式对视频进行分析处理&#xff0c;面临着效率低、成本高的困难。于是&#xff0c;MCA应运而生。它基于百度自研的视觉AI、ASR、NLP技术&#xff0c;为用户提供音视…

Vue 性能革命:揭秘前端优化的终极技巧;Vue优化技巧,解决Vue项目卡顿问题

目录 Vue优化路径 一、使用key 二、使用冻结对象 三、使用函数式组件 四、使用计算属性 五、使用非实时绑定的表单项 六、保持对象引用稳定 6.1、保持对象引用稳定定义 6.2、保持对象引用稳定与不稳定的例子 6.3、vue2判断数据是否变化是通过hasChanged函数实现的 ①…

2024年【四川省安全员B证】考试及四川省安全员B证考试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年【四川省安全员B证】考试及四川省安全员B证考试题&#xff0c;包含四川省安全员B证考试答案和解析及四川省安全员B证考试题练习。安全生产模拟考试一点通结合国家四川省安全员B证考试最新大纲及四川省安全员B证…

golang项目中gorm框架的配置和具体使用

最近在改造golang项目&#xff0c;从postgre数据库迁移到达梦数据库&#xff0c;我还想在改造后的项目使用 gorm 操作数据库&#xff0c;保持较小的改动。查找了不少资料&#xff0c;最终从以下两篇文章中借鉴了不少 1、Gorm 入门介绍与基本使用 这篇知乎文章详细介绍了 gorm 框…

C语言 -- 操作符详解​

C语言 -- 操作符详解​ 1. 操作符的分类2. 二进制和进制转换​2.1 2进制转10进制​2.1.1 10进制转2进制数字​ 2.2 2进制转8进制和16进制​2.2.1 2进制转8进制​2.2.2 2进制转16进制​ 3. 原码、反码、补码​4. 移位操作符​4.1 左移操作符​ 4.2 右移操作符​5. 位操作符&…

Symfony实战手册:PHP框架的高级应用技巧

引言 Symfony是一个功能强大且广泛应用于PHP应用程序开发的框架&#xff0c;它提供了许多高级特性和工具&#xff0c;可以帮助开发人员更高效地构建和管理复杂的Web应用程序。以下是Symfony框架的几个关键方面及其高级应用技巧&#xff1a; 1. 路由和控制器 Symfony的路由组…

suricata7 rule格式

suricata 7.0.5 suricata rule由三部分组成&#xff0c; action, header, options action,决定当前规则匹配上后需要执行的动作header,定义当前规则的协议&#xff0c;IP地址&#xff0c;端口&#xff0c;方向options,定义了具体的规则 一、 action 合法的action值有&#x…

Linux_共享内存通信

目录 1、共享内存原理 2、申请共享内存 2.1 ftok 2.2 测试shmget、ftok 2.3 查看系统下的共享内存 3、关联共享内存 3.1 测试shmat 4、释放共享内存 4.1 测试shmctl 5、实现共享内存通信 6、共享内存的特性 结语 前言&#xff1a; 在Linux下&#xff0c;有一…

爆!Java高级特性之Stream API详解

爆&#xff01;Java高级特性之Stream API详解 Java 8引入的Stream API可以说是一个革命性的特性,让我们告别了又臭又长的for循环,迎来了函数式编程的春天。今天就让我们来一起深入了解这个让人又爱又恨的Stream API吧! 什么是Stream? Stream就像一个高级的迭代器,允许我们以…

分支与循环

目录 1. if语句 1&#xff09;if 2) else 3&#xff09;分支中包含多条语句 4&#xff09;if嵌套 2.关系操作符 3.条件操作符 4.逻辑操作符&#xff1a;&& || ! 1) 逻辑取反运算符 !​编辑 2 与运算符​编辑 3) 或运算符​编辑 4) 闰年的判断 5) 短路 …

LangChain 概述 (模块索引)

文章目录 一、下载二、核心功能1、流式传输 streaming 三、LCEL四、组成部分1、Promp template2、Example selectors (示例选择器)3、Chat models (聊天模型)4、Messages (消息)5、LLMs (大语言模型) 一、下载 二、核心功能 其中包括以下内容&#xff1a; 从模型中返回结构化的…

若依 Vue 前端分离 3.8.8 版中生成的前端代码中关于下拉框只有下拉箭头的问题

生成代码修改前 <el-form-item label"课程学科" prop"subject"><el-select v-model"queryParams.subject" placeholder"请选择课程学科" clearable><el-optionv-for"dict in course_subject":key"dict…

Mysql中常用函数的使用示例

场景 基础知识回顾&#xff1a;mysql中常用函数的使用示例。 注&#xff1a; 博客&#xff1a;霸道流氓气质-CSDN博客 实现 数学函数 -- ABS(x)返回x的绝对值 SELECT ABS(-1),ABS(2); -- PI()返回圆周率 SELECT PI(); -- SQRT(x)返回非负数x的二次方根 SELECT SQRT(4); -…

【博士每天一篇文献-算法】Adult neurogenesis acts as a neural regularizer

阅读时间&#xff1a;2023-12-20 1 介绍 年份&#xff1a;2022 作者&#xff1a;Lina M. Tran&#xff0c;Adam Santoro&#xff0c;谷歌DeepMind 期刊&#xff1a; Proceedings of the National Academy of Sciences 引用量&#xff1a;13 代码&#xff1a;https://github.c…

A4-C四驱高防轮式巡检机器人

在当今数字化和智能化迅速发展的时代&#xff0c;旗晟智能带来了一款革命性的创新产品——A4-C四驱高防轮式巡检机器人。这款机器人以其卓越的性能和多功能性&#xff0c;为工业巡检领域带来了全新的解决方案。 一、产品亮点 1、四驱动力与高防护设计 四驱高防轮式巡检机器人…

ASUS/华硕枪神4 G532L G732L系列 原厂win10系统 工厂文件 带F12 ASUS Recovery恢复

华硕工厂文件恢复系统 &#xff0c;安装结束后带隐藏分区&#xff0c;一键恢复&#xff0c;以及机器所有驱动软件。 系统版本&#xff1a;Windows10 原厂系统下载网址&#xff1a;http://www.bioxt.cn 需准备一个20G以上u盘进行恢复 请注意&#xff1a;仅支持以上型号专用…

GPT-2怎么做翻译任务?

首先需要知道的是GPT-2无论在训练还是推理过程都是只使用了transformer decoder&#xff0c;并没有使用encoder结构&#xff0c;那么它是怎么做的翻译任务呢&#xff1f; 使用transformer encoderdecoder的著名架构有&#xff1a; 最原始的transformer model&#xff08;Atte…

计算机应用数学--第一次作业

第一次作业计算题编程题 &#xff08;20分&#xff09; 第一次作业 计算题 &#xff08;20分&#xff09;求 E ( X ) E(X) E(X)&#xff0c; V a r ( X ) Var(X) Var(X) &#xff08;1&#xff09; X X X 服从 [ a , b ] [a,b] [a,b] 均匀分布。 &#xff08;2&#xff09;…

操作系统期末必考概念大纲(整理·全)

第一章 1、 操作系统的概念 2、 计算机发展的四个阶段 3、 手工操作阶段、批处理系统阶段、多道程序系统阶段、分时操作系统阶段、通用操作系统阶段 4、 批处理系统&#xff08;联机、脱机&#xff09; 5、 操作系统的6个基本类型 6、 多道批处理特征 7、 分时系统特点 8、 算法…