首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Java Collection(1)——List——ArrayList(顺序表)&LinkedList(链表)

Java Collection(1)——List——ArrayList(顺序表)&LinkedList(链表)

作者头像
用户11873138
发布2026-01-13 21:21:28
发布2026-01-13 21:21:28
60
举报

一.认识List

1.什么是List?

List是Java标准库中的一个接口,下面是List和其他接口/类的关系图

在这里插入图片描述
在这里插入图片描述

2.List常用方法简介

1.boolean add(E e)

在这里插入图片描述
在这里插入图片描述

2.void add(int index, E element)

在这里插入图片描述
在这里插入图片描述

3.E remove(int index)

在这里插入图片描述
在这里插入图片描述

4.boolean remove(Object o)

在这里插入图片描述
在这里插入图片描述

5.E get(int index)

在这里插入图片描述
在这里插入图片描述

6.E set(int index, E element)

在这里插入图片描述
在这里插入图片描述

7.void clear()

在这里插入图片描述
在这里插入图片描述

8.boolean contains(Object o)

在这里插入图片描述
在这里插入图片描述

9.int indexOf(Object o)

在这里插入图片描述
在这里插入图片描述

10.int lastIndexOf(Object o)

在这里插入图片描述
在这里插入图片描述

11.List< E > subList(int fromIndex, int toIndex)

在这里插入图片描述
在这里插入图片描述

因为List是接口,不能直接实例化对象,所以以上的方法只能通过实现List的对象来调用,这里只是了解,后面会模拟实现上面的方法

二.ArrayList(顺序表)

1.什么是ArrayList?

简介: ArrayList是Java标准库中的一个类,实现了List接口。顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储,在数组上完成数据的增删查改 特点:

代码语言:javascript
复制
 动态大小:
代码语言:javascript
复制
 快速随机访问:通过下标访问元素,时间复杂度是O(1)
代码语言:javascript
复制
 存储任意类型的数据:支持存储任意类型的数据,包括基本数据类型(自动装箱)
代码语言:javascript
复制
 线程不安全:Vector(使用了synchronized)可以看作是ArrayList的线程安全版本,线程安全问题看我相关的博文

2.模拟实现ArrayList

1.创建ArrayList 实际上就是创建一个数组

在这里插入图片描述
在这里插入图片描述

2.添加元素

在这里插入图片描述
在这里插入图片描述

3.在pos位置添加元素

在这里插入图片描述
在这里插入图片描述

4.判断某元素是否存在

在这里插入图片描述
在这里插入图片描述

5.找到目标元素下标

在这里插入图片描述
在这里插入图片描述

6.判断输入下标是否合法

在这里插入图片描述
在这里插入图片描述

7.获取pos位置的元素

在这里插入图片描述
在这里插入图片描述

8.将pos位置元素改为value

在这里插入图片描述
在这里插入图片描述

9.删除第一次出现的目标元素

在这里插入图片描述
在这里插入图片描述

10.清空顺序表

在这里插入图片描述
在这里插入图片描述

3.模拟实现的ArrayList的源码

代码语言:javascript
复制
class MyArrayList<T>{
    private int[] array;
    private int usedSize;
    private static final int DefaultSize = 2;
    //无参构造方法
    public MyArrayList(){
        this.array = new int[DefaultSize];
    }
    //有参构造方法
    public MyArrayList(int initCapacity){
        this.array = new int[initCapacity];
    }
    //打印
    public void show(){
        for (int i = 0; i < this.usedSize; i++) {
            System.out.print(this.array[i]+" ");
        }
        System.out.println();
    }
    //判断是否满了
    public Boolean isFull(){
        if (this.array.length == this.usedSize)
            return true;
        else
            return false;
    }
    //从最后一个位置添加元素
    public void add(int data){
        if (isFull()){
            this.array = Arrays.copyOf(this.array,this.array.length*2);
        }
        this.array[this.usedSize] = data;
        this.usedSize++;
    }
    //在pos位置添加元素
    public void add(int pos,int data){
        if (pos < 0 || pos > this.usedSize)
            throw new exception(pos+"位置不合法");
        if (isFull()){
            this.array = Arrays.copyOf(this.array,this.array.length*2);
        }
        for (int i = this.usedSize-1; i >= pos ; i--) {
            this.array[i+1] = this.array[i];
        }
        this.array[pos] = data;
        this.usedSize++;
    }
    //判断某元素是否存在
    public boolean contains(int find){
        for (int i = 0; i < this.usedSize; i++) {
            if (this.array[i] == find){
                return true;
            }
        }
        return false;
    }
    //找到目标元素下标
    public int indexOf(int find){
        for (int i = 0; i < this.usedSize; i++) {
            if (this.array[i] == find){
                return i;
            }
        }
        return -1;
    }
    //判断输入下标是否合法
    public  Boolean check(int pos){
        if (pos < 0 || pos > this.usedSize - 1){
            return false;
        }
        return true;
    }
    //获取pos位置的元素
    public int get(int pos){
        if (!check(pos)){
            throw new exception(pos+"位置不合法");
        }
        return this.array[pos];
    }
    //将pos位置元素改为value
    public void set(int pos,int value){
        if (!check(pos)){
            throw new exception(pos+"位置不合法");
        }
        this.array[pos] = value;
    }
    //删除第一次出现的目标元素
    public void del(int toDel){
        int dex = indexOf(toDel);
        if (dex == -1){
            System.out.println("没有要删除的元素");
            return;
        }
        for (int i = dex; i < this.usedSize-1; i++) {
            this.array[i] = this.array[i+1];
        }
        //引用类型
        /*this.array[usedSize-1] = null;*/
        this.usedSize--;
    }
    //获取顺序表长度
    public int size(){
        return this.usedSize;
    }
    //清空顺序表
    public void clear(){
        //引用类型
        /*for (int i = 0; i < this.usedSize; i++) {
            this.array[i] = null;
        }*/
        this.usedSize = 0;
    }
}

三.LinkedList(链表)

1.什么是线性表?

简介: 线性表是n个具有相同特性的数据元素的有限序列,线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的空间(如下图)

在这里插入图片描述
在这里插入图片描述

每个节点有两个域,一个存放数据/值(value),一个存放下一个节点的地址(address)。通过上一个节点能够找到下一个节点,而且这些节点在物理结构上不一定是连续的,这就叫做线性表/链表。

2.链表的分类

从方向上来讲有三种类型

代码语言:javascript
复制
 单向链表(如上图):每个节点只有一个指向下一个节点的指针,最后一个节点的指针指向null。单向链表只能从头到尾进行遍历
代码语言:javascript
复制
 双向链表:每个节点有两个指针,一个指向前一个节点,一个指向下一个节点。双向链表可以从任意一个节点开始,向前或向后进行遍历
代码语言:javascript
复制
 循环链表:链表的最后一个节点指向头节点,形成一个环。循环链表可以是单向的,也可以是双向的

从头节点的区别来讲有两个类型

代码语言:javascript
复制
 带头链表:带头链表是指在链表的第一个节点之前增加一个特殊的节点,称为头结点。这个头结点通常不存储实际的数据,仅作为链表的起始点
代码语言:javascript
复制
 不带头链表(如上图):链表中所有节点的结构是一致的,第一个节点是不确定的(后面讲到头插法就明白了)

通过以上排列组合,一共可以得到六种类型的链表

代码语言:javascript
复制
 单向带头链表
代码语言:javascript
复制
 单向不带头链表
代码语言:javascript
复制
 双向带头链表
代码语言:javascript
复制
 双向不带头链表
代码语言:javascript
复制
 循环带头链表
代码语言:javascript
复制
 循环不带头链表

本文主要介绍单向不带头链表,只要搞清楚这一个类型的原理,其他类型都是同理

3.单向不带头链表模拟实现

1.创建链表

在这里插入图片描述
在这里插入图片描述

2.求链表长度

在这里插入图片描述
在这里插入图片描述

3.头插法

在这里插入图片描述
在这里插入图片描述

4.尾插法

在这里插入图片描述
在这里插入图片描述

头插法不需要判断当前链表是否为空,但是尾插法需要 因为不论链表是否为空,头插法不会触发空指针异常;但当链表为空时,尾插法如果不进行判断,就会触发空指针异常 5.在pos位置添加元素

在这里插入图片描述
在这里插入图片描述

6.删除出现的第一个key目标

在这里插入图片描述
在这里插入图片描述

7.删除出现的所有key目标

在这里插入图片描述
在这里插入图片描述

8.清空链表

在这里插入图片描述
在这里插入图片描述

4.模拟实现的单向链表源码

代码语言:javascript
复制
public class MySingleList {
    //内部类
    static class ListNode{
        public int val;
        public ListNode next;

        public ListNode(int val){
            this.val = val;
        }
    }
    //
    private ListNode head;
    //创建链表
    public void createList(){
        ListNode node1 = new ListNode(1);
        ListNode node2 = new ListNode(2);
        ListNode node3 = new ListNode(3);
        ListNode node4 = new ListNode(4);
        ListNode node5 = new ListNode(5);

        node1.next = node2;
        node2.next = node3;
        node3.next = node4;
        node4.next = node5;
        this.head = node1;
    }
    //打印链表
    public void disPlay(){
        ListNode cur = head;
        if (head == null){
            System.out.println("链表为空");
            return;
        }
        while (cur != null){
            System.out.print(cur.val+" ");
            cur = cur.next;
        }
        System.out.println();
    }
    //求链表长度
    public int getSize(){
        int count = 0;
        ListNode cur = head;
        while (cur != null){
            cur = cur.next;
            count++;
        }
        return count;
    }
    //查找key
    public Boolean contains(int key){
        ListNode cur = head;
        while (cur != null){
            if (cur.val == key){
                return true;
            }
            cur = cur.next;
        }
        return false;
    }
    //头插法
    public void addFirst(int data){
        ListNode node = new ListNode(data);
        node.next = head;
        head = node;
    }
    //尾插法
    public void addLast(int data){
        ListNode node = new ListNode(data);
        ListNode cur = head;
        if (cur == null){
            head = node;
            return;
        }
        while (cur.next != null){
            cur = cur.next;
        }
        cur.next = node;
    }
    //在pos位置添加元素,第一个节点下标为0,假如输入pos=2,那么该节点插入后就是链表的第2个节点
    public void addPos(int pos,int data){
        if (pos < 0 || pos > getSize()){
            System.out.println("插入位置不合法");
            return;
        }
        //pos == 0,直接使用头插法
        if(pos == 0){
            addFirst(data);
            return;
        }
        //pos == getSize(),使用尾插法
        if (pos == getSize()){
            addLast(data);
            return;
        }
        ListNode cur = toFindAddPosSubOne(pos);
        ListNode node = new ListNode(data);
        if (cur != null) {
            node.next = cur.next;
            cur.next = node;
        }
    }
    //第一个节点下标为0,找到pos下标的前一个节点,并且返回该节点的地址
    private ListNode toFindAddPosSubOne(int pos){
        if (pos < 0 || pos > getSize()){
            System.out.println("输入的位置不合法");
            return null;
        }
        ListNode cur = head;
        while ( pos - 1 != 0){
            cur = cur.next;
            pos--;
        }
        return cur;
    }
    //删除出现的第一个key目标
    public void delFirstKey(int key){
        if(head == null){
            System.out.println("链表为空");
            return;
        }
        if (head.val == key){
            head = head.next;
        }
        ListNode cur = toFindDel(key);
        if (cur == null){
            System.out.println("没有你要删除的数字");
            return;
        }
        cur.next = cur.next.next;
    }
    //找到要删除节点的前一个节点
    private ListNode toFindDel(int key){
        ListNode cur = head;
        while (cur != null){
            if (cur.next.val == key){
                return cur;
            }
            cur = cur.next;
        }
        return null;
    }
    //删除出现的所有key目标
    public void delAllKey(int key){
        if(head == null){
            System.out.println("链表为空");
            return;
        }
        while (head.val == key){
            head = head.next;
        }
        ListNode cur = head;
        ListNode del = cur.next;
        int count = 0;
        while (del != null){
            if (del.val != key){
                del = del.next;
                cur = cur.next;
            }else {
                cur.next = del.next;
                del = del.next;
                count++;
            }
        }
        if (count == 0){
            System.out.println("没有你要删除的数字");
        }
    }
    //清空链表
    public void clear(){
        head = null;
    }
}

四.顺序表VS链表

存储方式

代码语言:javascript
复制
 顺序表:使用连续的内存空间存储元素
代码语言:javascript
复制
 链表:使用非连续的内存空间存储元素

访问方式

代码语言:javascript
复制
 顺序表:支持随机访问,通过下标/索引在O(1)时间内访问
代码语言:javascript
复制
 链表:不支持随机访问,通过遍历链表访问元素(O(n)时间复杂度)

删除和插入

代码语言:javascript
复制
 顺序表:需要移动大量元素,例如:删除下标为2的元素,后面的元素都需要向前覆盖
代码语言:javascript
复制
 链表:头插法/尾插法,删除头/尾节点时间复杂度是O(1),虽然其他位置的插入和删除时间复杂度也是O(n),但不需要大量地移动元素

容量

代码语言:javascript
复制
 顺序表:自动扩容
代码语言:javascript
复制
 链表:没有容量这个概念

适用场景

代码语言:javascript
复制
 顺序表:元素高效存储+频繁访问
代码语言:javascript
复制
 链表:任意位置插入和删除频繁
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-10-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一.认识List
    • 1.什么是List?
    • 2.List常用方法简介
  • 二.ArrayList(顺序表)
    • 1.什么是ArrayList?
    • 2.模拟实现ArrayList
    • 3.模拟实现的ArrayList的源码
  • 三.LinkedList(链表)
    • 1.什么是线性表?
    • 2.链表的分类
    • 3.单向不带头链表模拟实现
    • 4.模拟实现的单向链表源码
  • 四.顺序表VS链表
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档