【JAVA】ArrayList与顺序表
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 前言
- 一、什么是线性表
- 二、顺序表
- 2.1 顺序表接口的实现
- 2.2 ArrayList简介
- 2.3 ArrayList的构造
- 2.4 ArrayList常见操作
- 2.5 ArrayList的遍历方式
- 2.6 ArrayList的扩容机制
- 2.7 ArrayList的应用
- 2.7.1 杨辉三角
- 2.7.2 简单的洗牌算法
- 2.8 小结
- 总结
前言
提示:这里可以添加本文要记录的大概内容:
在 Java 的集合框架中,ArrayList 是最常用的数据结构之一,它以动态数组为底层实现,提供了灵活的元素存取与自动扩容机制。而在数据结构课程中,顺序表(Sequential List) 作为线性表的一种典型实现,同样以数组为存储基础。两者在原理上有诸多相似之处,但在实现策略、内存管理、扩容机制以及时间复杂度控制等方面却各有差异。本文将从底层原理出发,深入分析 ArrayList 的实现细节,并将其与顺序表进行对比,以帮助读者更好地理解二者的联系与区别,从而在实际开发与算法设计中更合理地选择合适的数据结构。
提示:以下是本篇文章正文内容,下面案例可供参考
一、什么是线性表
线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列…
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。
二、顺序表
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。(即逻辑上连续,物理上也连续的数据结构)
2.1 顺序表接口的实现
2.2 ArrayList简介
在集合框架中,ArrayList是一个普通的类,实现了List接口,具体框架图如下:
总结:
- ArrayList是以泛型方式实现的,使用时必须要先实例化
- ArrayList实现了RandomAccess接口,表明ArrayList支持随机访问
- ArrayList实现了Cloneable接口,表明ArrayList是可以clone的
- ArrayList实现了Serializable接口,表明ArrayList是支持序列化的
- 和Vector不同,ArrayList不是线程安全的,在单线程下可以使用,在多线程中可以选择Vector或者CopyOnWriteArrayList
- ArrayList底层是一段连续的空间,并且可以动态扩容,是一个动态类型的顺序表
2.3 ArrayList的构造
package demo2;import java.util.ArrayList;public class Test {public static void main(String[] args) {ArrayList<Integer> list = new ArrayList<>();list.add(1);list.add(2);list.add(3);list.add(4);list.add(5);System.out.println(list);ArrayList<Integer> list2 = new ArrayList<>(list);list2.set(2,50);System.out.println(list2);ArrayList<String> list3 = new ArrayList<>(2);list3.add("hhh");list3.add("ddd");list3.add("aaa");System.out.println(list3);}
}
2.4 ArrayList常见操作
ArrayList虽然提供的方法比较多,但是常用方法如下所示,需要用到其他方法时,同学们自行查看ArrayList的帮助文档。
package demo3;import demo1.MyArrayList;import java.util.ArrayList;
import java.util.List;public class Test {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();ArrayList<String> list2 = new ArrayList<>(list);list2.add("hhh");list2.add("hhh");list2.add("hhh");list.add("dzj");list.add("hzp");list.add("xrx");list.add("xx");list.add("lyy");list.add("lwm");list.add(1,"sb");System.out.println(list);list.addAll(4,list2);System.out.println(list);list.remove(1);System.out.println(list);System.out.println(list.get(6));list.set(6,"xxx");System.out.println(list);list.clear();System.out.println(list);list.addAll(list2);System.out.println(list);System.out.println(list.contains("hh"));System.out.println(list.indexOf("hhh"));System.out.println(list.lastIndexOf("hhh"));list.addAll(list2);list.addAll(list2);list.addAll(list2);System.out.println(list);list.clear();list.add("1");list.add("2");list.add("3");list.add("4");list.add("5");System.out.println(list);List<String> sub=list.subList(1,4);sub.set(0,"dzj");System.out.println(list);System.out.println(sub);List<String> newList=new ArrayList<>(list.subList(1,4));System.out.println(newList);}
}
其中对于subList方法有一个小坑,请看如下代码:
list.add("1");list.add("2");list.add("3");list.add("4");list.add("5");System.out.println(list);List<String> sub=list.subList(1,4);sub.set(0,"dzj");System.out.println(list);System.out.println(sub);
也就是说sub和原来的list实际上用的是同一块空间,这和我们的正常认知有所不同,所以这通常是一种不安全的方式,请安全保险的方式如下:
List<String> newList=new ArrayList<>(list.subList(1,4));System.out.println(newList);
2.5 ArrayList的遍历方式
ArrayList 可以使用三方式遍历:for循环+下标、foreach、使用迭代器
package demo4;import java.util.ArrayList;
import java.util.Iterator;public class Test {public static void main(String[] args) {ArrayList<Integer> list = new ArrayList<>();list.add(1);list.add(2);list.add(3);list.add(4);list.add(5);for (int i = 0; i < list.size(); i++) {System.out.print(list.get(i)+" ");}System.out.println();for (Integer x : list) {System.out.print(x+" ");}System.out.println();Iterator<Integer> it = list.iterator();while(it.hasNext()){System.out.print(it.next()+" ");}}
}
2.6 ArrayList的扩容机制
关于这一点只需要了解以下内容即可。
无参创建的 ArrayList 初始并不开辟实际存储空间,只有在第一次添加元素时才分配默认容量(10个),后续扩容为1.5倍扩容。
具体的实现细节可以参照其源码,这一点大家自己感兴趣的可以自行查阅。
2.7 ArrayList的应用
2.7.1 杨辉三角
杨辉三角
class Solution {public List<List<Integer>> generate(int numRows) {List<List<Integer>> list=new ArrayList<>();for (int i = 0; i < numRows; i++) {list.add(new ArrayList<>());}for (int i = 0; i < numRows; i++) {for (int j = 0; j <=i; j++) {if(j==0||j==i){list.get(i).add(1);}else{int x = list.get(i-1).get(j);int y = list.get(i-1).get(j-1);list.get(i).add(x+y);}}}return list;}}
2.7.2 简单的洗牌算法
package demo5;public class Card {int rank;String suit;public Card(int rank, String suit){this.rank=rank;this.suit=suit;}@Overridepublic String toString() {return "{" +rank +", " + suit + '\'' +'}';}
}
package demo5;import java.util.ArrayList;
import java.util.List;
import java.util.Random;public class CardDemo {public static final String[] suits={"♠", "♥", "♣", "♦"};List<Card> deck = new ArrayList<>(52);public void createDeck(){for (int i = 0; i < suits.length; i++) {for (int j = 0; j < 13; j++) {Card card = new Card(j+1, suits[i]);deck.add(card);}}}public void swapCards(int i,int j){Card temp = deck.get(i);deck.set(i, deck.get(j));deck.set(j, temp);}public void shuffleDeck(){Random random=new Random();int count = 1;for (int i = deck.size()-1; i >0 ;i--) {int j = random.nextInt(deck.size()-count);swapCards(i,j);count++;}}
}
package demo5;import java.util.ArrayList;
import java.util.List;public class Test {public static void main(String[] args) {CardDemo cardDemo=new CardDemo();cardDemo.createDeck();System.out.println(cardDemo.deck);cardDemo.shuffleDeck();System.out.println(cardDemo.deck);List<List<Card>> persons=new ArrayList<>();persons.add(new ArrayList<>());persons.add(new ArrayList<>());persons.add(new ArrayList<>());for (int i = 0; i < 10; i++) {for (int j = 0; j < persons.size(); j++) {persons.get(j).add(cardDemo.deck.get(i));cardDemo.deck.remove(i);}}for(List<Card> person:persons){System.out.println(person);}System.out.println(cardDemo.deck);}
}
2.8 小结
1. ArrayList底层使用连续的空间,任意位置插入或删除元素时,需要将该位置后序元素整体往前或者往后搬移,故时间复杂度为O(N)
2. 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。
3. 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。
总结
通过对比可以发现,顺序表是理论基础,而 ArrayList 是工程化实现。顺序表强调抽象数据类型的逻辑结构和基本操作原理,而 ArrayList 则在此基础上进行了面向对象与动态内存管理的优化,具备更高的灵活性与可扩展性。理解 ArrayList 的底层机制不仅有助于掌握其性能特征(如时间复杂度与扩容策略),也能加深对数组式线性存储结构的理解。无论是数据结构学习者还是 Java 开发者,深入理解二者的内在联系,都能在实际编程中写出更高效、更稳定的代码。