【Java】顺序表ArrayList

文章目录

  • 一、顺序表
  • 二、ArrayList 的简介
  • 三、ArrayList 的使用
    • 3.1 构造方法
    • 3.2 常见操作
    • 3.3 遍历方法
    • 3.4 扩容机制
  • 四、ArrayList 的模拟实现
  • 五、ArrayList 的使用案例
    • 5.1 扑克牌案例
    • 5.2 杨辉三角案例
  • 六、ArrayList 存在的问题


一、顺序表

顺序表(Sequential List)是一种基本的数据结构,它由一组连续的存储空间(例如数组)来表示线性表的数据结构。顺序表中的元素在内存中是按照其逻辑顺序依次储存的。

顺序表具有以下特点:

  1. 元素在内存中的储存是连续的,可以通过下标直接访问和定位元素。

  2. 元素之间的逻辑关系通过元素在顺序表中的位置来表示。

  3. 顺序表的长度是固定的,即在创建时需要指定最大的容量,不能动态的增长或者缩小。

总的来说,顺序表的优点就能访问元素快速、定位方便,适合频繁访问和索引的场景。但是,由于顺序表的长度是固定的,插入和删除元素都可能会移动大量的元素,造成了效率低下的问题。因此如果需要频繁的进行插入和删除操作,就应该考虑其他数据结构,比如链表了。

二、ArrayList 的简介

ArrayList是Java集合框架中的一个类,它实现了List接口,提供了动态数组的功能。它是一个可变长度的数组,可以根据需要自动调整容量。

ArrayList的继承体系结构图如下:

ArrayList的继承和实现关系:

  • 类继承关系:

    • ArrayList类继承自AbstractList类。
    • AbstractList类继承自AbstractCollection类。
    • AbstractCollection类继承自Object类。
  • 接口实现关系:

    • ArrayList类实现了List接口。
    • List接口继承自Collection接口。
    • Collection接口继承自Iterable接口。
    • Iterable接口继承自java.lang.Iterable接口。

以下是每个类和接口的简要说明:

  • ArrayList类:实现了动态数组的功能,可以根据需要自动调整容量。
  • AbstractList类:提供了List接口的一些抽象方法的实现,为实现List接口的类提供了基本的功能。
  • AbstractCollection类:提供了Collection接口的一些抽象方法的实现,为实现Collection接口的类提供了基本的功能。
  • Object类:是Java中所有类的根类。
  • List接口:表示有序的元素集合,定义了基本的操作方法,如添加、删除、查找、获取等。
  • Collection接口:表示一组对象的集合,定义了基本的操作方法,如添加、删除、查找等。
  • Iterable接口:表示可迭代的对象,定义了迭代元素的方法,用于支持迭代操作。

这些类和接口的层次结构使得ArrayList具备了列表的功能,并可以与其他集合类和接口进行交互和使用。ArrayList作为Java集合框架中的一部分,提供了丰富的功能和灵活性,使得开发人员可以方便地处理和操作动态数组。

三、ArrayList 的使用

3.1 构造方法

ArrayList类提供了多个构造方法来创建对象。以下是ArrayList的常用构造方法及其示例:

  1. ArrayList():创建一个空的ArrayList对象。
ArrayList<String> list = new ArrayList<>();
  1. ArrayList(int initialCapacity):创建一个具有指定初始容量的ArrayList对象。
ArrayList<Integer> list = new ArrayList<>(10);
  1. ArrayList(Collection<? extends E> c):创建一个包含指定集合元素的ArrayList对象。
ArrayList<String> list1 = new ArrayList<>(Arrays.asList("apple", "banana", "orange"));
ArrayList<String> list2 = new ArrayList<>(list1);

示例说明:

  • 在示例1中,创建了一个空的ArrayList对象,用于存储String类型的元素。
  • 在示例2中,创建了一个具有初始容量为10的ArrayList对象,用于存储Integer类型的元素。
  • 在示例3中,通过传入一个已有的集合,创建了一个包含集合元素的ArrayList对象。

注意:在示例3中,使用了Arrays.asList()方法将元素直接传递给ArrayList的构造方法,或者直接将另一个ArrayList对象传递给构造方法。这样做可以快速初始化ArrayList,并将已有的元素添加到新创建的ArrayList中。

3.2 常见操作

ArrayList类提供了一系列方法来操作和处理列表中的元素。以下是ArrayList的一些常用方法及其示例:

  1. 添加元素:
  • add(E element):在列表末尾添加一个元素。
  • add(int index, E element):在指定位置插入一个元素。
ArrayList<String> list = new ArrayList<>();list.add("apple");
list.add("banana");
list.add(1, "orange");System.out.println(list); // 输出:[apple, orange, banana]
  1. 获取元素:
  • get(int index):获取指定位置的元素。
ArrayList<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");String fruit = list.get(1);
System.out.println(fruit); // 输出:banana
  1. 删除元素:
  • remove(int index):删除指定位置的元素。
  • remove(Object element):删除第一个匹配的元素。
ArrayList<String> list = new ArrayList<>();
list.add("apple");
list.add("orange");
list.add("banana");list.remove(1); // 删除位置为1的元素
System.out.println(list); // 输出:[apple, banana]list.remove("banana"); // 删除元素为"banana"
System.out.println(list); // 输出:[apple]
  1. 修改元素:
  • set(int index, E element):替换指定位置的元素。
ArrayList<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");list.set(1, "orange"); // 替换位置为1的元素
System.out.println(list); // 输出:[apple, orange]
  1. 元素数量和判断:
  • size():返回列表中的元素数量。
  • isEmpty():判断列表是否为空。
ArrayList<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");System.out.println(list.size()); // 输出:2
System.out.println(list.isEmpty()); // 输出:false

3.3 遍历方法

在ArrayList中,有多种遍历方法可以访问和处理列表中的元素。以下是几种常用的ArrayList遍历方法:

  1. 使用for循环遍历:
ArrayList<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");
list.add("orange");for (int i = 0; i < list.size(); i++) {String fruit = list.get(i);System.out.println(fruit);
}
  1. 使用增强型for循环(foreach循环)遍历:
ArrayList<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");
list.add("orange");for (String fruit : list) {System.out.println(fruit);
}
  1. 使用迭代器(Iterator)遍历:
ArrayList<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");
list.add("orange");Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {String fruit = iterator.next();System.out.println(fruit);
}

3.4 扩容机制

在Java中,ArrayList的扩容机制是在元素数量超过当前容量时自动进行扩容。扩容操作是为了保证ArrayList能够容纳更多的元素,并提高性能。

ArrayList的扩容机制如下:

  1. 当添加新元素时,ArrayList会检查当前元素数量是否已经达到了数组的容量。
  2. 如果元素数量已经达到了容量上限,ArrayList会根据一定的策略进行扩容。
  3. 扩容操作会创建一个新的更大容量的数组,并将原数组中的元素复制到新数组中。
  4. 扩容后,ArrayList的容量会增加,可以继续添加新的元素。

具体的扩容策略如下:

  1. 初始容量为10。当第一个元素被添加时,容量增加到10。
  2. 扩容时,新容量的大小为当前容量的1.5倍。
  3. 如果当前容量的1.5倍小于所需容量,则新容量的大小为所需容量。

需要注意的是,扩容操作会涉及到数组的拷贝,因此在大规模数据的插入时,可能会有一定的性能开销。为了避免频繁的扩容操作,可以在创建ArrayList时指定一个合适的初始容量,尽量接近预期的元素数量。

以下是一个示例,展示了ArrayList的扩容过程:

ArrayList<Integer> list = new ArrayList<>();System.out.println("初始容量:" + list.size()); // 输出:初始容量:0for (int i = 0; i < 20; i++) {list.add(i);System.out.println("元素数量:" + list.size() + ",容量:" + list.toArray().length);
}/* 输出:
元素数量:1,容量:10
元素数量:2,容量:10
...
元素数量:11,容量:15
元素数量:12,容量:15
...
元素数量:20,容量:30
*/

从示例中可以看到,当元素数量超过当前容量时,ArrayList会按照扩容策略自动增加容量,并将元素复制到新的数组中。在扩容过程中,容量会逐步增加,以满足添加更多元素的需求。

四、ArrayList 的模拟实现

import java.util.Arrays;public class MyArrayList {private int[] elem; // 数组private int usedSize; // 记录有效的数据个数private static final int DEFAULT_SIZE = 10; // 默认数组大小public MyArrayList(){this.elem = new int[DEFAULT_SIZE];}// 新增元素,默认在数组最后新增public void add(int data){// 1. 检查当前顺序表是否满了if(isFull()){// 2. 如果满了就进行扩容elem = Arrays.copyOf(this.elem, 2 * this.elem.length);}this.elem[usedSize] = data;this.usedSize++;}public boolean isFull(){return size() >= elem.length;}// 在pos位置新增元素public void add(int pos, int data) throws PosErrorException{// 判断 pos 的合法性if(pos > this.size() || pos < 0){throw new PosErrorException("pos 不合法");}// 判断当前顺序表是否满了if(isFull()) {// 扩容elem = Arrays.copyOf(this.elem, 2 * this.elem.length);}for (int i = size() - 1; i >= pos; i--) {elem[i + 1] = elem[i];}elem[pos] = data;usedSize++;}// 获取pos下标的元素public int get(int pos){if(pos < 0 || pos >= this.size()){throw new PosErrorException("pos 不合法");}return elem[pos];}// 判断是否包含某个元素public boolean contains(int toFind){for (int i = 0; i < this.size(); i++) {if(toFind == elem[i]){return true;}}return false;}// 查找某个元素对应的位置public int indexOf(int toFind){for (int i = 0; i < this.size(); i++) {if(toFind == elem[i]){return i;}}return -1;}// 给pos位置的元素设置为valuepublic int setValue(int pos, int value){if(pos < 0 || pos >= this.size()){throw new PosErrorException("pos 不合法");}int tmp = elem[pos];elem[pos] = value;return tmp;}// 删除第一次出现的元素 keypublic boolean remove(int key){int index = indexOf(key);if (index == -1)return false;for (int i = index; i < size() - 1; i++) {elem[i] = elem[i + 1];}this.usedSize--;return true;}// 获取顺序表长度public int size() {return this.usedSize;}// 清空顺序表内容public void clear(){this.elem = new int[DEFAULT_SIZE];this.usedSize = 0;}// 打印数组中的元素public void display(){for (int i = 0; i < this.usedSize; i++) {System.out.print(elem[i] + " ");}System.out.println();}public boolean isEmpty(){return this.usedSize == 0;}
}

五、ArrayList 的使用案例

5.1 扑克牌案例

class Poker {private int rank; // 面值private String suit; // 花色public Poker(int rank, String suit) {this.rank = rank;this.suit = suit;}public int getRank() {return rank;}public void setRank(int rank) {this.rank = rank;}public String getSuit() {return suit;}public void setSuit(String suit) {this.suit = suit;}@Overridepublic String toString() {return "【" + suit + rank + "】"; // 【♠ 2】}
}public class Pokers {private static final String[] SUITS = new String[]{"♥", "♠", "♣", "♦"};public ArrayList<Poker> buyPokers(){ArrayList<Poker> pokers = new ArrayList<>();for (int i = 0; i < 4; ++i){for(int j = 1; j <= 13; ++j){String suit = SUITS[i];pokers.add(new Poker(j, suit));}}return pokers;}private void swap(ArrayList<Poker> pokerList, int i, int j){Poker tmp = pokerList.get(i);pokerList.set(i, pokerList.get(j));pokerList.set(j, tmp);}public void shuffle(ArrayList<Poker> pokerList){Random random = new Random();for(int i = pokerList.size() - 1; i > 0; --i){int index = random.nextInt(i); // [0, i)swap(pokerList, i, index);}}public static void main(String[] args) {Pokers pokers = new Pokers();ArrayList<Poker> pokerList = pokers.buyPokers();System.out.println("刚买回来的牌:" + pokerList);System.out.println();pokers.shuffle(pokerList);System.out.println("洗过的牌:" + pokerList);ArrayList<Poker> hand1 = new ArrayList<>();ArrayList<Poker> hand2 = new ArrayList<>();ArrayList<Poker> hand3 = new ArrayList<>();ArrayList<ArrayList<Poker>> hands = new ArrayList<>();hands.add(hand1);hands.add(hand2);hands.add(hand3);// 三个人轮流摸五次牌for(int i = 0; i < 5; ++i){for(int j = 0; j < 3; ++j){ArrayList<Poker> tmpHand = hands.get(j);tmpHand.add(pokerList.remove(0));}}for (int i = 0; i < 3; ++i){System.out.println("第" + (i + 1) + "个人:" + hands.get(i));}System.out.println("剩余的牌:" + pokerList);System.out.println(pokerList.size());}
}

上述代码片段包含两个类:PokerPokers

Poker类表示一张扑克牌,具有rank(面值)和suit(花色)属性,以及对应的访问方法和重写的toString()方法。

Pokers类包含了一些扑克牌游戏的操作方法。其中:

  • buyPokers()方法用于生成一副完整的扑克牌,并将它们放入一个ArrayList中。
  • swap()方法用于交换扑克牌列表中两张牌的位置。
  • shuffle()方法使用Fisher-Yates(Knuth洗牌算法)算法对扑克牌列表进行洗牌。
  • main()方法包含了一个简单的扑克牌游戏的示例逻辑。首先,生成一副扑克牌,并打印出初始顺序。然后,进行洗牌操作,并打印洗牌后的顺序。接下来,模拟三个人轮流摸牌的过程,并打印每个人手中的牌以及剩余的牌。

这个示例代码演示了如何使用Poker类和Pokers类来模拟扑克牌游戏的一些操作,包括生成扑克牌、洗牌和发牌等。运行代码可以看到相应的输出结果。

5.2 杨辉三角案例

下面是一个使用 ArrayList 实现的杨辉三角案例,根据输入的 n 生成 n 行的杨辉三角:

import java.util.ArrayList;
import java.util.List;public class PascalTriangle {public List<List<Integer>> generatePascalTriangle(int numRows) {List<List<Integer>> triangle = new ArrayList<>();for (int i = 0; i < numRows; i++) {List<Integer> row = new ArrayList<>();for (int j = 0; j <= i; j++) {if (j == 0 || j == i) {row.add(1);} else {List<Integer> prevRow = triangle.get(i - 1);int num = prevRow.get(j - 1) + prevRow.get(j);row.add(num);}}triangle.add(row);}return triangle;}public static void main(String[] args) {int numRows = 5;PascalTriangle pascalTriangle = new PascalTriangle();List<List<Integer>> triangle = pascalTriangle.generatePascalTriangle(numRows);for (List<Integer> row : triangle) {System.out.println(row);}}
}

在上面的代码中,generatePascalTriangle 方法用于生成杨辉三角。它通过遍历每一行,然后遍历每一行的每个位置,根据杨辉三角的性质将相应的数字添加到列表中。最后,将每一行的列表添加到杨辉三角的二维列表中。

main 方法中,我们指定了要生成的杨辉三角的行数 numRows 为 5,并通过调用 generatePascalTriangle 方法生成杨辉三角。然后,我们逐行打印杨辉三角的结果。

运行示例代码会输出以下结果:

[1]
[1, 1]
[1, 2, 1]
[1, 3, 3, 1]
[1, 4, 6, 4, 1]

六、ArrayList 存在的问题

关于 ArrayList 的一些常见限制和缺点:

  1. 顺序表中间/头部的插入删除,时间复杂度为 O(N):ArrayList 的底层实现是基于数组,当需要在中间或头部插入或删除元素时,需要进行数据的搬移操作,这会导致时间复杂度为 O(N),其中 N 是元素的个数。因此,在需要频繁进行中间或头部的插入删除操作时,ArrayList 的性能可能不如链表(LinkedList)等数据结构。

  2. 增容需要申请新空间,拷贝数据,释放旧空间:ArrayList 在容量不足时会自动进行扩容操作,一般是通过申请更大的数组空间,并将旧数据拷贝到新空间中,然后释放旧空间。这个过程需要耗费一定的时间和资源。因此,在频繁插入大量数据或需要快速扩容的场景下,ArrayList 可能会有一定的性能开销。

  3. 增容一般是呈2倍的增长,可能会有空间浪费:ArrayList 在进行扩容时,通常会将容量扩大一倍。这是为了避免频繁的扩容操作,提高性能。然而,当在容量不断增长的情况下,如果仅插入一小部分元素并停止插入,就会导致一定的空间浪费。例如,如果当前容量为 100,满了以后增容到 200,然后再插入了 5 个数据,那么就会浪费 95 个数据的空间。这种情况下,可以考虑在需要精细控制内存使用的场景下使用其他数据结构,如 LinkedList。

总结来说,ArrayList 在访问元素和尾部插入删除等操作上具有较好的性能,但在中间/头部的插入删除、频繁的扩容和大量数据插入情况下可能存在一些性能和空间方面的问题。在实际使用时,需要根据具体场景的需求进行选择合适的数据结构。

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

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

相关文章

js vuejs dagre-d3绘制流程图实用指南 有向图可视化

写在前面 之前有小伙伴问我如何使用 D3 在前端绘制流程图,今天在这里给安排上,与大家分享。 明确一点,只要你的数据计算能力足够强,使用原生D3绘制流程图绝对可以的,但是,为了让大家更容易上手,避免重复造轮子,给大家推荐一个专门绘制流程图的 D3 插件 dagre-d3。 首…

基于Flask+Bootstrap+机器学习的世界杯比赛预测系统

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

Springboot分布式事务

一、先了解什么是本地事务 1. 概念 本地事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器位于同一节点相同数据库上。 又称为传统事务。它是一个操作序列&#xff0c;这些操作要么都执行&#xff0c;要么都不执行&#xff0c;是一个不可分割的工作单位。例…

你真的不想知道怎么用ai绘画图片生成图片吗?

亲爱的二次元迷们&#xff0c;你是否曾经梦想过能够画出自己心中的二次元角色&#xff0c;让他们跃然纸上、生动活泼地展现在世人面前&#xff1f;但是&#xff0c;面对空白的画板和一支笔&#xff0c;我们有时会感到无从下手&#xff0c;毫无艺术细胞可言。不要失望&#xff0…

贵阳阿里云代理商:阿里云中国区副总裁李国欢:数字经济与实体经济如何融合

中国青年报客户端讯&#xff08;中青在线记者 于璧嘉&#xff09;6月21日&#xff0c;在第六届世界智能大会即将召开之际&#xff0c;阿里云中国区副总裁李国欢在接受媒体专访时表示&#xff1a;“数字经济已经成为社会生产力革命的一个重要生产要素。” 6月21日&#xff0c;阿…

如何批量将PDF转换为图片?

在生活工作中&#xff0c;我们会处理很多电子合同。这些电子合同一般是PDF格式&#xff0c;不但存储空间大&#xff0c;且预览起来不太便捷&#xff0c;需要我们转换为图片格式更方便预览。如果人工一一处理比较繁琐复杂&#xff0c;有没有什么方案可以快速将pdf转换为图片呢&a…

[STC32F12K54入门第三步]USART1+Modbus RTU从机

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 一、Modbus RTU是什么?二、Modbus RTU程序展示1.串口配置2.Timer定时器配置3.配置CRC16校验位和Modbus RTU发送函数4.主函数5.效果展示总结提示:以下是本篇文章正文内容,下面案例可供参考 一、…

基于Java连锁经营商业管理系统设计实现(源码+lw+部署文档+讲解等)

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

Linux之Kafka保姆式详细安装教程

下载Kafka 《Kafka官网下载》 注意&#xff1a;下载的是二进制文件&#xff0c;不要下载源码&#xff01;这里可以采用第三方下载工具加速下载&#xff0c;如&#xff1a;迅雷等 上传到Linux服务器的/data/目录下进行解压 tar -zxvf是解压文件命令&#xff0c;-C表示把解压…

Airtest:Windows桌面应用自动化测试四【Airtest之python本地环境安装、独立IDE运行】

Airtest之python本地环境安装、独立IDE运行 一、环境配置二、安装Airtest三、安装poco四、常见问题4.1若运行代码时&#xff0c;在cv2模块报ImportError: DLL load failed: 找不到指定模块的错&#xff0c;有几种解决方案&#xff1a;4.1.1.本问题的根本原因应该是DLL文件的缺失…

Elasticsearch:install

ElasticSearch Elasticsearch 是一个分布式、高扩展、高实时的搜索与数据分析引擎。 Elasticsearch结合Kibana、Logstash、Beats&#xff0c;也就是elastic stack(ELK)。被广泛应用在日志分析、实时监控&#xff08;CPU、Memory、Program&#xff09;等领域。 elasticsearch是…

2023年SCI影响因子(JCR2022)正式公布

2023年6月28日&#xff0c;Clarivate Analytics&#xff08;科睿唯安&#xff09;发布最新的《期刊引证报告》&#xff08;Journal Citation Reports&#xff0c;简称JCR&#xff09;&#xff0c;刷新SCI期刊2022年影响因子(IF)。该指数也备受访问学者、联培博士及博士后研究者…