数据结构-ArrayList和顺序表

1.线性表

线性表是n个具有相同类型的数据元素所组成的有限序列,当n=0时,线性表为一个空表。

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

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

2.顺序表 

2.1 什么是顺序表 

顺序表是一段连续的存储单元来依次存储线性表中的数据元素。一般情况下采用数组存储。

2.2 顺序表的特点 

1.存储连续:顺序表的存储单元在内存中是连续的,这意味着每个元素的地址都是相邻连续的

2.随机访问:由于存储单元是连续的,可以通过元素的下标直接访问该位置的元素,时间复杂度为O(1)

3.存储密度高:顺序表的存储单元只存储数据元素本身,没有额外的存储开销

2.3 实现一个顺序表

public class MySequentialList{
    private int[] array;
    private int size;
    //默认构造方法
    MySequentialList(){}
    //将顺序表的容量设为initCapacity
    MySequentialList(int initCapacity){}
    //在数组末尾新增一个元素
    public void add(int data){}
    //在pos位置新增一个元素
    public void add(int pos,int data){}
    //判断是否包含某个元素
    public boolean contains(int toFind){}
    //查找某个元素对应的位置
    public int indexOf(int toFind){}
    //获取pos位置上的元素
    public int get(int pos){}
    //给pos位置上的元素设置为value
    public void set(int pos,int value){}
    //删除第一次出现的关键字key
    public void remove(int toRemove){}
    //获取顺序表的长度
    public int size(){}
    //清空顺序表
    public void clear(){}
}

实现的思路:

1.MySequentialList(int initCapacity)

只需要将数组初始化大小为initCapacity即可

2.add(int data)

在数组最后的位置新增元素,要注意数组是否已满,如果已满,就要对数组进行扩容操作

3.add(int pos,int data)

首先要判断插入位置pos是否合法,如果pos小于0或pos大于数组的长度,则位置不合法要进行异常处理。如果位置合法,还要判断数组是否已满,进行插入操作时,要注意先将原pos以及pos后面的元素全都向后移动一位,再进行插入操作,如果直接插入,则只是单纯的元素覆盖。插入后,数组的长度加1

4.contains(int toFind)

判断是否包含某个元素,只需要遍历数组并进行比较,看数组中是否有存在要查找的元素,如果有,则返回true,没有则返回false

5.indexOf(int toFind)

遍历数组,看数组中是否存在要查找的元素,如果存在,则返回给元素位置的下标,如果不存在,则返回-1

6.get(int pos)

获取pos位置的元素,首先要判断pos位置是否合法,如果pos小于0或者pos大于数组的长度,则pos不合法,返回-1。如果合法,直接返回下标为pos的元素

7.set(int pos,int value)

给pos位置的元素设置为value,单纯的进行对应下标元素覆盖即可

8.remove(int toRemove)

删除第一次出现关键字key,遍历数组,看key是否存在。如果不存在,则返回-1。如果存在,只需要将该位置后面的所有元素先前移动一位即可

9.size()

直接返回数组的有效长度,有效长度指的是数组中有效元素的个数,不是单纯的数组长度

10.clear()

清空顺序表,重新初始化数组,并将数组的长度置为0

我们后续以ArrayList的实现为例。

3.ArrayList

3.1 什么是ArrayList

在集合框架中,ArrayList是 Java 标准库中的一个非常常用的类,它实现了 List接口,提供了动态数组的功能。 ArrayList内部使用数组来存储元素,因此它具备顺序表的所有特点,是顺序表的一种实现。

 

说明:

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

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

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

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

5.ArrayList是线程不安全的,在单线程下可以使用

6.ArrayList是通过动态的数组实现的,是一段连续的空间,并且可以进行扩容

3.2 ArrayList的构造方法

方法解释
ArrayList()无参构造
ArrayList(Collection<? extends E> c)利用其他Collection构建ArrayList
ArrayList(int initialCapacity)指定顺序表初始容量

 代码演示:

public class Test {
    public static void main(String[] args) {
        //无参构造
        List<Integer> list1=new ArrayList<>();
        
        //利用其他Collection构建ArrayList
        ArrayList<Integer> arrayList=new ArrayList<>();
        List<Integer> list2=new ArrayList<>(arrayList);
       
        //指定顺序表容量
        List<Integer> list3=new ArrayList<>(10);
        
    }
}

3.3 ArrayList的常见操作

ArrayList常见的方法

方法解释
boolean add(E,e)在尾部插入元素e
void add(int index,E e)将元素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 e)将下表为index位置的元素设置为e
void clear()清空所有元素
boolean contains(Object o)判断o是否在线性表中
int indexOf(Object o)返回第一个o所在的下标
int lastIndexOf(Object o)返回最后一个o所在的下标
List<E> subList(int fromIndex,int toIndex)截取部分list

代码演示:

import java.util.ArrayList;
import java.util.List;

public class Test {
    public static void main(String[] args) {
        List<Integer> list=new ArrayList<>();
        System.out.println(list);
        //在尾部插入元素
        list.add(1);
        list.add(5);
        list.add(3);
        list.add(5);
        list.add(10);
        System.out.println(list);
        //在下标为2的位置增加元素
        list.add(2,4);
        System.out.println(list);
        //删除下标为2的元素
        list.remove(2);
        System.out.println(list);
        //删除遇到的第一个5元素
        Integer a=5;
        list.remove(a);
        System.out.println(list);
        //获取下标为1的元素
        int e=list.get(1);
        System.out.println(e);
        //将下标为1的元素设置为99
        list.set(1,99);
        System.out.println(list);
        //判断5是否在线性表中
        System.out.println(list.contains(5));
        //返回第一个5所在的下标
        e=list.indexOf(5);
        System.out.println(e);
        //返回最后一个5所在的下标
        e= list.lastIndexOf(5);
        System.out.println(e);
        //截取部分list,左闭右开
        List<Integer> newList=new ArrayList<>();
        newList=list.subList(1,3);
        System.out.println(newList);
        //将newList中的元素全部添加入list中
        list.addAll(newList);
        System.out.println(list);
        //清空list
        list.clear();
        System.out.println(list);
    }
}

3.4 ArrayList的遍历 

ArrayList的遍历方式主要分为3种:

1.for循环搭配下标

2.foreach

3.使用迭代器

public class Test {
    public static void main(String[] args) {
        List<String> list=new ArrayList<>();
        list.add("hajimi");
        list.add("is");
        list.add("a");
        list.add("cat");
        //使用for循环搭配下标
        for(int i=0;i<list.size();i++){
            System.out.print(list.get(i)+" ");
        }
        System.out.println();
        //使用foreach遍历
        for(String s:list){
            System.out.print(s+" ");
        }
        System.out.println();
        //使用迭代器遍历
        Iterator<String> it=list.listIterator();
        while(it.hasNext()){
            System.out.print(it.next()+" ");
        }
    }
}

4.深度理解ArrayList 的扩容机制

观察原码:

int DEFAULT_CAPACITY表示默认的容量大小

Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA表示默认的空间

Object[] elementData表示存放元素的空间

 ArrayList的无参构造函数,将elementData初始化为DEFAULTCAPACITY_EMPTY_ELEMENTDATA,这是一个空数组,用于创建一个具有默认容量为 10的空ArrayList

ArrayList的add方法首先调用ensureCapacityInternal(size+1),size表示当前顺序表的有效长度,size+1表示添加元素后应有的长度 

 size+1的值传递给ensureCapacityInternal的形参minCapacity,ensureCapacityInternal先调用calculateCapacity(Object[] element,int minCapacity)传入存放元素的空间elementData和minCapacity

calculateCapacity(Object[] elementData,int minCapacity)用来计算ArrayList需要增长到的最小容量。判断elementData是否等于DEFAULTCAPACITY_EMPTY_ELEMENTDATA,如果相等则意味着ArrayList当前没有分配任何容量,是空的。在这种情况下,方法返回DEFAULT_CAPACITY(10)和minCapacity中的较大值。如果element不是空数组,则意味着ArrayList已经有了一些容量,这种情况下,直接返回minCapacity,可以保证它至少增长到minCapacity即可

 calculateCapacity的返回值传递给ensureExplicitCapacity的形参minCapacity,判断当前是否需要扩容,如果minCapacity大于数组的长度,则表示需要进行扩充

 ArrayList的扩容函数grow(int minCapacity)用于增加ArrayList的内部数组elementData的容量,使其至少容纳minCapacity个元素。

int newCapacity=oldCapacity+(oldCapacity >> 1)计算新的容量,通常是当前容量的1.5倍

if(newCapacity - minCapacity < 0)判断新容量是否满足最小的需求,如果计算出的容量仍然小于最小的容量,则将newCapacity设置为minCapacity

if(newCapacity-MAX_ARRAY_SIZE > 0)检查新容量是否超出最大数组的大小,如果超过了调用hugeCapacity处理这种情况(将新容量设为Integer.MAX_VALUES)

elementData = Arrays.copyOf(elementData,newCapacity)使用copyOf创建一个新的数组,并将就数组中的元素复制到新数组中。

总结:

1.首先判断是否需要进行扩容,如果需要进行扩容,调用grow方法

2.计算扩容所需的最小的容量

初步预估按照1.5倍大小进行扩容

如果用户所需大小大于预估的1.5倍,则按照用户所需大小进行扩容

扩容前检查是否可以扩容成功,防止太大导致扩容失败

3.使用Arrays.copyOf进行扩容

5.实现一个ArrayList

package datastructure;

import java.util.Arrays;

public class MyArrayList {
    public int[] elem;
    public int usedSize;//0
    //默认容量
    private static final int DEFAULT_SIZE = 2;

    public MyArrayList() {
        this.elem = new int[DEFAULT_SIZE];
    }

    /**
     * 打印顺序表:
     *   根据usedSize判断即可
     */
    public void display() {

        for(int i=0;i<this.usedSize;i++){
            System.out.print(elem[i]+" ");
        }
        System.out.println();
    }

    // 新增元素,默认在数组最后新增
    public void add(int data) {
        if(isFull()){
            //扩容
            this.elem=Arrays.copyOf(this.elem,2*this.elem.length);
        }
        this.elem[this.usedSize]=data;
        this.usedSize++;
    }

    /**
     * 判断当前的顺序表是不是满的!
     * true:满   false代表空
     */
    public boolean isFull() {
        if(this.usedSize==this.elem.length){
            return true;
        }
        return false;
    }


    private boolean checkPosInAdd(int pos) {
        if(pos<0||pos>usedSize){
            System.out.println("位置不合法");
            return false;
        }
        return true;//合法
    }

    // 在 pos 位置新增元素
    //移动数据,从后向前防止数值被覆盖
    public void add(int pos, int data) {
        if(pos<0||pos>this.usedSize){
            System.out.println("位置不合法");
            return;
        }
        if(isFull()){
            //扩容
            this.elem=Arrays.copyOf(this.elem,2*this.elem.length);
        }
        for(int i=this.usedSize-1;i>=pos;i--){
            this.elem[i+1]=this.elem[i];
        }
        this.elem[pos]=data;
        usedSize++;
    }

    // 判定是否包含某个元素
    public boolean contains(int toFind) {
        for(int i=0;i<this.usedSize;i++){
            if(elem[i]==toFind){
                return true;
            }
        }
        return false;
    }
    // 查找某个元素对应的位置
    public int indexOf(int toFind) {
        if(isEmpty()){
            System.out.println("表中没有元素");
            return -1;
        }
        for(int i=0;i<usedSize;i++){
            if(elem[i]==toFind){
                return i;
            }
        }
        System.out.println("没找到");
        return -1;
    }

    // 获取 pos 位置的元素
    public int get(int pos) {
        if(pos<0&&pos>=usedSize){
            System.out.println("输入不合法!");
            return -1;
        }
        if(!isEmpty()){
            return elem[pos];
        }
        return -1;
    }

    private boolean isEmpty() {
        if(usedSize==0){
            return true;
        }
        return false;
    }
    // 给 pos 位置的元素设为【更新为】 value
    public void set(int pos, int value) {
        if(pos<0||pos>=usedSize)
            return;
        elem[pos]=value;
    }

    /**
     * 删除第一次出现的关键字key
     * @param key
     */
    public void remove(int key) {
        int index=indexOf(key);
        if(index==-1){
            return;
        }
        for(int i=index;i<usedSize-1;i++){
            this.elem[i]=this.elem[i+1];
        }
        this.usedSize--;
    }

    // 获取顺序表长度
    public int size() {
       return this.usedSize;
    }

    // 清空顺序表
    public void clear() {
        this.usedSize=0;
    }
}

6.ArrayList的特点

1.基于数组实现:

ArrayList使用一个动态数组来存储元素

2.动态数组容量

ArrayList可以根据添加元素的情况进行自动扩容,默认情况下是按照当前容量的1.5倍进行扩容

3.随机访问效率高,时间复杂度为O(1)

ArrayList基于数组实现,支持随机访问,时间复杂度为O(1)

4.插入和删除操作时间复杂度为O(n)

在ArrayList的中间位置插入或删除元素效率不高,这些操作会移动插入点之后的所有元素,时间复杂度为O(n),但是在末尾插入或删除元素的时间复杂度为O(1)

5.允许有重复的元素

6.允许存储null元素

7.ArrayList是线程不安全的

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

计算机视觉算法实战——人类情感识别(主页有源码)

✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连✨ ​​​​​​​​​​​​​​​​​​ 1. 引言✨✨ 人类情感识别&#xff08;Facial Expression Recognition, FER&#xff09;是计算机视觉领…

08_游戏启动逻辑

1.GameRoot.cs 控制 服务层Svc.cs 和业务层Sys.cs 的初始化 创建脚本GameRoot.cs&#xff08;游戏入口 已进入就初始化各个系统&#xff09; 创建资源加载服务.cs Res 将服务层Svc设置成单例类所以需要挂载在GameRoot身上&#xff0c;这样就可以通过GameRoot来调各个服务 接…

当使用 npm 时,出现 `certificate has expired` 错误通常意味着请求的证书已过期。

当使用 npm 时&#xff0c;出现 certificate has expired 错误通常意味着请求的证书已过期。这可能是由于以下几种情况&#xff1a; 网络代理问题&#xff1a;如果使用了网络代理&#xff0c;代理服务器的证书可能过期或配置有误。系统时间错误&#xff1a;系统时间不准确可能导…

2024年,我的技术探索与成长之路

2024年&#xff0c;我的技术探索与成长之路 2024年已经过去&#xff0c;作为一名技术爱好者和写作者&#xff0c;我回顾了过去一年在博客上记录的点滴&#xff0c;感慨良多。这一年&#xff0c;我不仅见证了技术的飞速发展&#xff0c;也在不断学习和实践中找到了自己的成长方向…

ASP.NET Blazor部署方式有哪些?

今天我们来说说Blazor的三种部署方式&#xff0c;如果大家还不了解Blazor&#xff0c;那么我先简单介绍下Blazor Blazor 是一种 .NET 前端 Web 框架&#xff0c;在单个编程模型中同时支持服务器端呈现和客户端交互性&#xff1a; ● 使用 C# 创建丰富的交互式 UI。 ● 共享使用…

【LeetCode】--- MySQL刷题集合

1.组合两个表&#xff08;外连接&#xff09; select p.firstName,p.lastName,a.city,a.state from Person p left join Address a on p.personId a.personId; 以左边表为基准&#xff0c;去连接右边的表。取两表的交集和左表的全集 2.第二高的薪水 &#xff08;子查询、if…

【C++】模板(进阶)

本篇我们来介绍更多关于C模板的知识。模板初阶移步至&#xff1a;【C】模板&#xff08;初阶&#xff09; 1.非类型模板参数 1.1 非类型模板参数介绍 模板参数可以是类型形参&#xff0c;也可以是非类型形参。类型形参就是我们目前接触到的一些模板参数。 //类型模板参数 …

ASP .NET Core 学习(.NET9)配置接口访问路由

新创建的 ASP .NET Core Web API项目中Controller进行请求时&#xff0c;是在地址:端口/Controller名称进行访问的&#xff0c;这个时候Controller的默认路由配置如下 访问接口时&#xff0c;是通过请求方法&#xff08;GET、Post、Put、Delete&#xff09;进行接口区分的&…

用于牙科的多任务视频增强

Multi-task Video Enhancement for Dental Interventions 2022 miccai Abstract 微型照相机牢牢地固定在牙科手机上&#xff0c;这样牙医就可以持续地监测保守牙科手术的进展情况。但视频辅助牙科干预中的视频增强减轻了低光、噪音、模糊和相机握手等降低视觉舒适度的问题。…

一部手机如何配置内网电脑同时访问内外网

做过运维的朋友都知道&#xff0c;最麻烦的是运维电脑不能远程&#xff0c;每次都得现场进行维护&#xff0c;明明客户那边有可以访问内网的电脑&#xff0c;怎么操作能将这台电脑能访问跟到外网呢&#xff0c;这样不就能通过远程软件远程了吗&#xff1f;嘿嘿。按以下步骤试试…

基于STM32的智能门锁安防系统(开源)

目录 项目演示 项目概述 硬件组成&#xff1a; 功能实现 1. 开锁模式 1.1 按键密码开锁 1.2 门禁卡开锁 1.3 指纹开锁 2. 功能备注 3. 硬件模块工作流程 3.1 步进电机控制 3.2 蜂鸣器提示 3.3 OLED显示 3.4 指纹与卡片管理 项目源代码分析 1. 主程序流程 (main…

(三)线性代数之二阶和三阶行列式详解

在前端开发中&#xff0c;尤其是在WebGL、图形渲染、或是与地图、模型计算相关的应用场景里&#xff0c;行列式的概念常常在计算变换矩阵、进行坐标变换或进行图形学算法时被使用。理解二阶和三阶行列式对于理解矩阵运算、旋转、平移等操作至关重要。下面&#xff0c;我将结合具…

基于GRU实现股价多变量时间序列预测(PyTorch版)

前言 系列专栏:【深度学习:算法项目实战】✨︎ 涉及医疗健康、财经金融、商业零售、食品饮料、运动健身、交通运输、环境科学、社交媒体以及文本和图像处理等诸多领域,讨论了各种复杂的深度神经网络思想,如卷积神经网络、循环神经网络、生成对抗网络、门控循环单元、长短期记…

【EdgeAI实战】(1)STM32 边缘 AI 生态系统

【EdgeAI实战】&#xff08;1&#xff09;STM32 边缘 AI 生态系统 【EdgeAI实战】&#xff08;1&#xff09;STM32 边缘 AI 生态系统 1. STM32 边缘人工智能1.1 X-CUBE-AI 扩展包1.2 STM32 AI Model Zoo1.3 ST AIoT Craft 2. STM32N6 AI 生态系统 (STM32N6-AI)2.1 STM32N6 AI 产…

DeepSeek-R1性能如何?如何使用DeepSeek-R1和o1 Pro模型

我们一起来看看DeepSeek-R1模型和OpenAI o1模型的能力如何&#xff1f;接下来&#xff0c;我们先看数据结果&#xff0c;然后再实际体验&#xff0c;我们今天就让他们写个python爬虫脚本来爬取所有有关孙颖莎和樊振东的相关报道和图片。 DeepSeek-R1 DeepSeek介绍自己说 &quo…

FunASR语言识别的环境安装、推理

目录 一、环境配置 1、创建虚拟环境 2、安装环境及pytorch 官网&#xff1a;pytorch下载地址 3、安装funasr之前&#xff0c;确保已经安装了下面依赖环境: python代码调用&#xff08;推荐&#xff09; 4、模型下载 5、启动funasr服务 二、 客户端连接 2.1 html连接 …

【Elasticsearch】 Ingest Pipeline `processors`属性详解

在Elasticsearch中&#xff0c;Ingest Pipeline 的 processors 属性是一个数组&#xff0c;包含一个或多个处理器&#xff08;processors&#xff09;。每个处理器定义了一个数据处理步骤&#xff0c;可以在数据索引之前对数据进行预处理或富化。以下是对 processors 属性中常见…

架构思考与实践:从通用到场景的转变

在当今复杂多变的商业环境中&#xff0c;企业架构的设计与优化成为了一个关键议题。本文通过一系列随笔&#xff0c;探讨了业务架构的价值、从通用架构到场景架构的转变、恰如其分的架构设计以及如何避免盲目低效等问题。通过对多个实际案例的分析&#xff0c;笔者揭示了架构设…

消息队列实战指南:三大MQ 与 Kafka 适用场景全解析

前言&#xff1a;在当今数字化时代&#xff0c;分布式系统和大数据处理变得愈发普遍&#xff0c;消息队列作为其中的关键组件&#xff0c;承担着系统解耦、异步通信、流量削峰等重要职责。ActiveMQ、RabbitMQ、RocketMQ 和 Kafka 作为市场上极具代表性的消息队列产品&#xff0…

win32汇编环境,怎么得到磁盘的盘符

;运行效果 ;win32汇编环境,怎么得到磁盘的盘符 ;以下代码主要为了展示一下原理&#xff0c;应用GetLogicalDrives、GetLogicalDriveStrings函数、屏蔽某些二进制位、按双字节复制内容等。以下代码最多查8个盘&#xff0c;即返回值中的1个字节的信息 ;直接抄进RadAsm可编译运行。…