【数据结构】ArrayList与顺序表

news2024/9/23 19:26:10

目录

1.List接口

2.线性表

3.顺序表

3.1常用方法 

3.2常用方法的实现

4.ArrayList

4.1构造方法

4.2遍历

4.3扩容

4.4CVTE面试题:删除相同字符

5.ArrayList的具体实现

5.1洗牌算法

5.2杨辉三角

6.ArrayList的优缺点


1.List接口

List 接口继承于 Collection 接口,Collection 接口继承于 Iterable 接口。

这三个接口中都有许多常用方法(点击Structure可以查看)。

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;

public class Test {
    List<Integer> stack = new Stack<>();//栈
    List<Integer> arrayList = new ArrayList<>();//顺序表
    List<Integer> linkList = new LinkedList<>();//链表
}

2.线性表

定义:n个具有相同特性的数据元素的有限序列,是一种在实际中广泛使用的数据结构。

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

逻辑上是线性结构,即连续的一条直线,但在物理结构上并不一定是连续的。

物理上存储时,通常以数组和链式结构的形式存储。

3.顺序表

定义:用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。逻辑上是连续的,物理上也是连续的。

即:通过方法操作数组

3.1常用方法 

方法说明
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<E> subList ( int fromIndex, int toIndex )截取部分List

 其中E为泛型。

注意subList 方法截取的是原来List对象中的地址,所以修改subList截取后的值会同时改变原来List中的值。

3.2常用方法的实现

下面方法ArrayList表中都有,我们自己来实现一下,更能理解里面具体的实现原理: 

package Demo1;

public class PosOutOfBoundsException extends RuntimeException{
    public PosOutOfBoundsException(String message) {
        super(message);
    }
}
package Demo1;

public class Test {
    public static void main(String[] args) {
        SeqList seqList = new SeqList();
        for (int i = 0; i < 10; i++) {
            seqList.add(i,i);
        }
        seqList.display();
        //System.out.println(seqList.get(6));//报错
        //……………………
    }
}
package Demo1;

import java.util.Arrays;

public class SeqList {
    private int[] elem;
    private int usedSize;//默认为0,记录当前顺序表中有几个有效的数据
    private static final int DEFAULT_CAPACITY = 3;//默认容量
    public SeqList() { //不带参数的构造方法
        this.elem = new int[DEFAULT_CAPACITY];//new一个数组
    }
    private boolean isFull() { //判断数组是否满了
        return this.usedSize == this.elem.length;
    }
    private boolean checkPos(int pos) { //判断获取的下标是否合法
        if (pos < 0 || pos >= this.usedSize) {
            return false;
        }
        return true;
    }
    private boolean isEmpty() { //判断顺序表是否为空数据
        return this.usedSize == 0;
    }
    private void resize() { //扩容
        this.elem = Arrays.copyOf(this.elem,2*this.elem.length);
        //copyOf开辟一个长度为2*elem.length的新数组并将原elem数组中的元素拷贝进去,再返回给elem
    }
    public void display() { //打印顺序表
        for (int i = 0; i < this.usedSize; i++) {
            System.out.println(this.elem[i]+" ");
        }
        System.out.println();
    }
    public void add(int data) { //新增元素,默认在已有数据后新增
        if (isFull()) { //如果满了,给数组扩容
            resize();
        }
        this.elem[this.usedSize] = data;
        this.usedSize++;
    }
    public void add(int pos,int data) { //在pos位置新增元素data
        if (isFull()) { //满了就扩容
            resize();
        }
        if (pos < 0 || pos > this.usedSize) { //注意这里与checkPos不同
            throw new PosOutOfBoundsException("add时位置不合法");
        }
        for (int i = this.usedSize-1; i >= pos ; i--) { //给data挪位置
            this.elem[i+1] = this.elem[i];
        }
        this.elem[pos] = data;//存data
        this.usedSize++;
    }
    public boolean contains(int toFind) { //判断是否包含某个元素
        for (int i = 0; i < this.usedSize; i++) {
            if (toFind == this.elem[i]) {
                return true;
            }
        }
        return false;
    }
    public int indexOf(int toFind) { //查找某个元素对应位置的下标
        for (int i = 0; i < this.usedSize; i++) {
            if (toFind == this.elem[i]) {
                return i;
            }
        }
        return -1;
    }
    public int get(int pos) { //获取pos位置处的元素
        if (checkPos(pos) == false) {
            throw new PosOutOfBoundsException("get时位置不合法");//自定义一个异常
        }
        return this.elem[pos];
    }
    public int size() { //获取当前顺序表长度
        return this.usedSize;
    }
    public void set(int pos,int value) { //将pos位置处的值更新为value
        if (checkPos(pos)) {
            throw new PosOutOfBoundsException("set时位置不合法");
        }
        this.elem[pos] = value;
    }
    public void remove(int key) { //删除第一次出现的关键字key
        if (isEmpty()) {
            return;
        }
        int index = indexOf(key); //查找key
        if (index == -1) { //没找到要删除的key
            return;
        }
        for (int i = index; i <this.usedSize-1; i++) {
            this.elem[i] = this.elem[i+1];
        }
        this.usedSize--;//这一步不能忘!
        //this.elem[usedSize] = null; 存储引用类型时要手动置空
    }
    public void clear() { //清空顺序表
        /*for (int i = 0; i < usedSize; i++) {
            this.elem[i] = null;
        }*/  //此处是当elem中存储引用类型数据时,清空时全部置为null
        this.usedSize = 0; //存数字时只需将有效元素个数置为0即可
    }
}

4.ArrayList

注意:1> ArrayList实现了List接口;

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

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

4> 实现了Cloneable接口,表明支持克隆

5> 实现了Serializable接口,表明支持序列化

6> ArrayList不是线程安全的,在单线程下可以使用,多线程中应选择 VectorCopyOnWriteArrayList

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

4.1构造方法

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

注意:上述第二个构造方法中,?是通配符,在之后的泛型进阶中会讲到;后面括号中代码意为参数必须为E本身或E的子类。 

public class Test {
    public static void main(String[] args) {
        List<Integer> list1 = new ArrayList<>();//构造一个空的顺序表
        List<Integer> list2 = new ArrayList<>(10);//构造一个初始容量为10的顺序表
        list2.add(1);
        list2.add(2);
        list2.add(3);
        ArrayList<Integer> list3 = new ArrayList<>(list2);//参数为本身或其子类时,可以直接赋值
    }
}

4.2遍历

四种遍历方法:sout直接打印,for循环,for-each循环,迭代器。

public class Test {
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        arrayList.add(1);
        arrayList.add(2);
        arrayList.add(3);
        //法一:sout直接输出
        System.out.println(arrayList);//[1, 2, 3]---原因:父类中重写了toString方法
        //法二:for循环
        for (int i = 0; i < arrayList.size(); i++) {
            System.out.print(arrayList.get(i)+" ");
        }
        System.out.println();
        //法三:for-each循环
        for (int x : arrayList) { //冒号前为类型和临时创建的变量,冒号后为需要遍历的对象
            System.out.print(x+" ");
        }
        System.out.println();
        //法四:迭代器(设计模式的一种)
        Iterator<Integer> iterator = arrayList.listIterator();//定义一个迭代器
        while (iterator.hasNext()) { //如果有下一个就打印下一个
            System.out.print(iterator.next()+" ");
        }
        System.out.println();
    }
}

4.3扩容

源码见下:

说明

1.当调用不带参数的构造方法时,底层的数组长度为0(空数组);

2.第一次add时,会给我分配大小为10的内存;

3.所需空间超过原有空间时,初步预估按照1.5倍大小扩容;

4.如果用户所需大小超过预估1.5倍大小,则按照用户所需大小扩容;

5. 使用copyOf进行扩容。

注意

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

2.扩容之前检测是否能扩容成功,防止太大导致扩容失败。

4.4CVTE面试题:删除相同字符

编程要求:删除第一个字符串中出现的第二个字符串中的字符。 

public class Test {
    //法一:ArrayList
    public static void func1(String str1,String str2) {
        List<Character> ret = new ArrayList<>();//定义一个ArrayList表用来存放结果
        for (int i = 0; i < str1.length(); i++) {
            char ch = str1.charAt(i);//遍历获取str1中的每个字符
            if (!str2.contains(ch+"")) { //如果str2不包含这个字符
                ret.add(ch);//将这个字符插入顺序表中
            }
        }
        for (int i = 0; i < ret.size(); i++) {
            System.out.print(ret.get(i));
        }
    }

    //法二:数组
    public static String func1(String str1,String str2) {
        String Tmp = str1;
        for (int i = 0; i < str2.length(); i++) {
            String tmp = String.valueOf(str2.charAt(i));
            Tmp = Tmp.replaceAll(tmp,"");
        }
        return Tmp;
    }

    public static void main(String[] args) {
        func("welcome to world","come");//wl t wrld
        String s = func1("welcome to world","come");
        System.out.println(s);//wl t wrld
    }
}

这里有一个编程技巧:因为 contains 的参数为 CharSequence,是字符串,而ch只是一个字符,此时将ch 加上一个"" 即可将字符变为字符串。

5.ArrayList的具体实现

5.1洗牌算法

Card.java

package Demo;
//这里面的所有代码都可以用Generate自动生成
public class Card { //定义牌
    private String suit;//花色
    private int rank;//数字
    public Card(String suit, int rank) {
        this.suit = suit;
        this.rank = rank;
    }
    public String getSuit() {
        return suit;
    }
    public void setSuit(String suit) {
        this.suit = suit;
    }
    public int getRank() {
        return rank;
    }
    public void setRank(int rank) {
        this.rank = rank;
    }
    @Override
    public String toString() {
        return suit+rank ;
    }
}

Test.java 

package Demo;

import java.util.*;

public class Test {
    private static final String[] SUITS = {"♥","♦","♠","♣"};//定义四个花色
    public static List<Card> buyCard() { //买牌(初始化牌)
        List<Card> cards = new ArrayList<>();//定义一个cards表
        for (int i = 0; i < SUITS.length; i++) {
            for (int j = 1; j <= 13; j++) {
                Card card = new Card(SUITS[i],j);//调用了Card的构造方法,分别new了一张牌
                cards.add(card);//将牌放入cards表里
            }
        }
        return cards;
    }
    public static void shuffle(List<Card> cards) { //洗牌
        //Collections.shuffle(cards); --- 库方法中有,这里我们自己实现:
        Random random = new Random();//new一个随机数对象
        for (int i = cards.size()-1; i > 0 ; i--) {
            int j = random.nextInt(i);//随机取一个[0,i)中的值(下标)
            Card tmp = cards.get(i);//在表中随机取一个值
            cards.set(i,cards.get(j));//利用set方法交换这两个值
            cards.set(j,tmp);
        }
    }
    public static void main(String[] args) {
        List<Card> cards = buyCard();//买牌(初始化牌)
        System.out.println("初始的牌:"+cards);
        shuffle(cards);//洗牌
        System.out.println("洗完的牌:"+cards);

        //定义三个表分别存放每个人揭的牌
        List<Card> hand1 = new ArrayList<>();
        List<Card> hand2 = new ArrayList<>();
        List<Card> hand3 = new ArrayList<>();

        //再定义一个表存放上面存牌的三个表(相当于二维数组)
        List<List<Card>> hand = new ArrayList<>();//注意这里List<>中的类型
        hand.add(hand1);
        hand.add(hand2);
        hand.add(hand3);

        //三个人轮流揭5张牌,注意不是一次性揭5张牌
        for (int i = 0; i < 5; i++) {
            for (int j = 0; j < 3; j++) {
                Card card = cards.remove(0);//揭牌,即每次拿走表中的第一张(0下标处的)牌
                hand.get(j).add(card);//相当于先拿到二维数组的行数,再去访问行里的元素
            }
        }

        System.out.println("第一个人的牌:"+hand1);
        System.out.println("第二个人的牌:"+hand2);
        System.out.println("第三个人的牌:"+hand3);
        System.out.println("剩下的牌"+cards);
    }
}

5.2杨辉三角

杨辉三角

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

public class Test {
    public static List<List<Integer>> generate(int numRows) { //这个类型相当于二维数组
        List<List<Integer>> ret = new ArrayList<>();//用来接收整个杨辉三角,相当于二维数组
        List<Integer> list = new ArrayList<>();//用来接收杨辉三角的每一行,相当于一维数组
        list.add(1);//第一行只有1
        ret.add(list);//将第一行放入"二维数组"中
        for (int i = 1; i < numRows; i++) { //循环add每一行
            List<Integer> curRow = new ArrayList<>();
            curRow.add(1);//每一行第一个元素均为1
            for (int j = 1; j < i; j++) { //这里add每一行中间的元素
                int value = ret.get(i-1).get(j)+ret.get(i-1).get(j-1);//!!!
                curRow.add(value);
            }
            curRow.add(1);//每一行最后一个元素均为1
            ret.add(curRow);//add完一行后再整行add进"二维数组"中
        }
        return ret;
    }
    public static void main(String[] args) {
        List<List<Integer>> list = new ArrayList<>();
        list = generate(5);
        System.out.println(list);
    }
}

6.ArrayList的优缺点

优点:根据指定下标 (索引) 去查找元素或更新元素的效率非常高,时间复杂度可为O(1)。

缺点:1> 每次插入或删除数据时,都需要移动数据,若要插到0下标位置或删除0下标数据,复杂度将达到O(n);2> 扩容时默认扩容1.5倍,当原空间很大而扩容空间只使用了一点时,将会浪费很多空间。

总结:顺序表适用于经常进行查找元素或更新元素的场景下使用,在另一些顺序表不适用的场景下,就需要 链表 出马了~~


下一篇文章就要探讨链表的奥秘了,狠狠期待住了!!

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

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

相关文章

3 天,入门 TAURI 并开发一个跨平台 ChatGPT 客户端

TAURI 是什么 TAURI 是一个使用 Rust 编写的程序框架&#xff0c;它允许我们使用 Web 技术和 Rust 语言构建跨端应用。它提供了大量特性&#xff0c;例如系统通知、网络请求、全局快捷键、本地文件处理等&#xff0c;它们都可以在前端通过 JavaScript 便捷的调用。 TAURI 应用…

《精英的傲慢:好的社会该如何定义成功》笔记与摘录

目录 作者简介 书内容简介 经典摘录 1、现状与现象 2、什么是优绩至上原则 3、对优绩至上原则赞同与否的讨论 4、 优绩至上原则存在的争议点 5、 作为哲学家&#xff0c;桑德尔从道德哲学角度的思考 6、作者对优绩制的批判 7、流动性与平等的关系 8、我们该如何摆脱优…

MyCat分片-垂直分库

文章目录 需求场景一、环境准备二、实现1.MyCat—schema.xml文件配置2.MyCat—server.xml文件配置3.MyCat启动4.MyCat登录5.创建表结构及数据导入 三、全局表配置全局表配置 此文档来源于网络,如有侵权&#xff0c;请联系删除&#xff01; 需求场景 在业务系统中&#xff0c;涉…

使用ChatGPT辅助学习——让你的学生主动找到学习的方法!

ChatGPT就像一座巨大的金矿&#xff0c;能挖到多少金子&#xff0c;完全取决于你的思维、认知和行动力。 当大部分人还在观望&#xff0c;或者拿着ChatGPT随便玩一玩的时候。 有的人&#xff0c;已经快速把它切入垂直领域&#xff0c;开始深耕。 如果你的孩子或者学生正在上初…

静态库和动态库的制作与使用

1.静态库的制作与使用 小知识&#xff1a;删除命令行&#xff0c;或者是配置好的路径之类的&#xff1a;退出编辑模式后&#xff1a;dd 保存并退出&#xff1a;退出编辑模式后&#xff0c;&#xff1a;wq (1)静态库的制作 1.首先生成你需要加入的文件的.O文件。使用如下代码 …

网络编程六--UDP服务器客户端

写在前面 UDP&#xff08;User Datagram Protocol&#xff09;称为用户数据报协议&#xff0c;是一种无连接的传输协议。 UDP的主要应用在即使丢失部分数据&#xff0c;也不影响整体效果的场景。例实时传输视频或音频时&#xff0c;即使丢失部分数据&#xff0c;也不会影响整…

C++11大杂烩

C11大杂烩 文章目录 C11大杂烩介绍语法统一的列表初始化&#xff1a;{}初始化initializer_list简化声明的方式autotypeid().name():获取类型名decltype nullptr范围for循环stl库中的一些变化arrayforward_list final和override右值引用和移动语义左值引用和右值引用 移动构造和…

有没有好用的UI在线设计工具?这5个设计师必备!

这篇文章介绍了 5 款在线UI设计工具&#xff0c;分别是即时设计、InVision Studio、Axure、Framer 和 Principle。其中&#xff0c;即时设计是一款次世代的在线协作UI设计工具&#xff0c;支持多人协同在线设计一键交付、插入交互式动画等功能&#xff0c;最近还更新了全球首款…

网络基础知识1—网络

文章目录 1.网络划分1.1局域网&#xff08;内网&#xff09;1.2广域网&#xff08;公网&#xff09; 2.网络的作用3.端口号3.1作用3.2两台主机中的进程传输数据3.3格式3.4注意 4.协议4.1概念4.2三要素4.3最终体现4.4作用 5.五元组5.1源IP5.2源端口5.3目的IP5.4目的端口5.5协议 …

MySQL中这14个神仙功能

1.group_concat 在我们平常的工作中&#xff0c;使用group by进行分组的场景&#xff0c;是非常多的。 比如想统计出用户表中&#xff0c;名称不同的用户的具体名称有哪些&#xff1f; 具体sql如下&#xff1a; select name from user group by name;但如果想把name相同的c…

IO 流学习总结

一&#xff1a;IO 流的概述 1. 什么是 IO 流&#xff1f; 存储和读取数据的解决方法 I&#xff1a;input O&#xff1a;output 流&#xff1a;像水流一样传输数据 2. IO 流的作用&#xff1f; 用于读写数据&#xff08;本地文件&#xff0c;网络&#xff09; 3. IO 流按…

三年亏百亿仍要造“跑车”,哪吒还有几次试错?

文丨智能相对论 作者丨leo陈 燃油车时代&#xff0c;国产品牌没有一款真正意义上成功的“低价跑车”&#xff0c;那在新能源时代&#xff0c;“电”是否可以创造这种可能&#xff1f; 第一个交出答卷的是哪吒汽车。不久前&#xff0c;哪吒发布首款纯电跑车“哪吒GT”&#x…

3个方法提高电脑运行速度,亲测有效!

案例&#xff1a;怎样提高电脑运行的速度&#xff1f; 【随着使用时间的增长&#xff0c;我的电脑运行速度越来越慢&#xff0c;这样我感到十分不方便和烦恼。有什么办法可以提高电脑的运行速度吗&#xff1f;】 在日常使用电脑过程中&#xff0c;我们难免会遇到电脑运行缓慢…

【C++】第13章: 类继承

文章目录 第十三章 类继承13.1 一个简单的基类13.1.1 派生一个类13.1.2 构造函数&#xff1a;访问权限的考虑13.1.3 使用派生类13.1.4 派生类和基类之间的特殊关系 13.2 继承&#xff1a;is-a关系13.3 多态公有继承13.4 静态联编与动态联编13.4.1 指针和引用类型的兼容性13.4.2…

Doris简介、部署、功能介绍以及架构设计

Doris简介、部署、功能介绍以及架构设计 1. Doris简介 Doris 中文官方文档&#xff1a;https://doris.apache.org/zh-CN/docs/dev/summary/basic-summary 1.1 Doris概述 ​ Apache Doris 是一个基于 MPP 架构的高性能、实时的分析型数据库&#xff0c;以极速易用的特点被人…

5.10晚间黄金CPI精准分析及多空交易策略

近期有哪些消息面影响黄金走势&#xff1f;本周黄金多空该如何研判&#xff1f; ​黄金消息面解析&#xff1a;周三&#xff08;5月10日&#xff09;亚欧盘中&#xff0c;现货黄金震荡下跌&#xff0c;现报2030美元/盎司&#xff0c;稍早一度触及2038美元/盎司高点。美联储理事…

day30_jdbc

今日内容 零、 复习昨日 一、作业 二、SQL注入 三、PreparedStatement 四、事务 五、DBUtil 零、 复习昨日 见晨考 一、作业 package com.qf.homework;import com.qf.entity.User;import java.sql.*; import java.text.SimpleDateFormat; import java.util.ArrayList; import …

Selenium技术在CentOS6.8系统的腾讯云服务器上的相关使用(Linux环境下)

目录 一、解释说明二、操作过程中Linux相关命令1、下载谷歌浏览器2、查看谷歌浏览器的版本3、下载对应版本的谷歌驱动&#xff08;或者本地上传&#xff09;4、解压下载的文件5、移动下载文件6、给予文件执行权限7、更新pip3到最高版本8、下载Selenium第三方库9、正式测试10、最…

Rust 快速入门60分① 看完这篇就能写代码了

Rust 一门赋予每个人构建可靠且高效软件能力的语言https://hannyang.blog.csdn.net/article/details/130467813?spm1001.2014.3001.5502关于Rust安装等内容请参考上文链接&#xff0c;写完上文就在考虑写点关于Rust的入门文章&#xff0c;本专辑将直接从Rust基础入门内容开始讲…

如何预测药品市场规模

药品市场规模预测是一个非常关键的步骤&#xff0c;可以帮助判断该项目是否值得投资或开发。以下是一些常见的方法&#xff1a; 药品市场规模可以细分为治疗领域市场规模、药品种类市场规模、区域市场规模、渠道市场规模、品牌市场规模、性质市场规模等。这些规模的了解是一个非…