篇章四 数据结构——顺序表

目录

1.List

[1.1 什么是 List](#1.1 什么是 List)

[1.2 常见接口介绍](#1.2 常见接口介绍)

[1.3 List的使用](#1.3 List的使用)

[2. ArrayList 与 顺序表](#2. ArrayList 与 顺序表)

[2.1 线性表](#2.1 线性表)

[2.2 顺序表](#2.2 顺序表)

1.什么是顺序表

2.为什么要有顺序表?

3.自己实现一个add方法

[2.3 ArrayList简介](#2.3 ArrayList简介)

[2.4 ArrayList使用](#2.4 ArrayList使用)

[1. ArrayList的构造](#1. ArrayList的构造)

[1.1 ArrayList 基础属性的含义​编辑](#1.1 ArrayList 基础属性的含义编辑)

[1.2 ArrayList() 解析](#1.2 ArrayList() 解析)

[1.3 ArrayList(int initialCapacity) 解析](#1.3 ArrayList(int initialCapacity) 解析)

[1.4 ArrayList(Collection c) 解析](#1.4 ArrayList(Collection c) 解析)

[2. ArrayList 的常见操作](#2. ArrayList 的常见操作)

[2.1 add(int index, E element)](#2.1 add(int index, E element))

​编辑

[2.2 remove的区分](#2.2 remove的区分)

[2.3 subList 存在问题](#2.3 subList 存在问题)

[3. ArrayList的遍历](#3. ArrayList的遍历)

[4. ArrayList的扩容](#4. ArrayList的扩容)

5.ArrayList的练习

[5.1 用于字符串](#5.1 用于字符串)

[5.2 杨辉三角:](#5.2 杨辉三角:)

[5.3 洗牌算法](#5.3 洗牌算法)

[2.5. ArrayList的缺点](#2.5. ArrayList的缺点)


1.List

1.1 什么是 List

在 集合框架 中,List 是一个接口,继承自 Collection

Collection 也是一个接口,该接口 规范 了后序容器中常用的一些方法, 具体如下:

Iterable 也是一个接口,表示实现该接口的类是可以 逐个元素遍历 的,具体如下:

List的官方文档

但是站在 数据结构 的角度来看,List 就是一个 线性表

即:n 个具有 相同类型元素 的 有限序列

在该序列上可以执行 增删改查 以及 遍历 等操作。

1.2 常见接口介绍

List中提供了好多的方法,具体如下:

虽然方法比较多,但是常用方法如下:

增:add addAll

删:remove clear

改:set

查:get indexOf lastIndexOf

裁:subList

1.3 List的使用

注意:List是个接口,并不能直接用来实例化。

如果要使用,必须去实例化List的实现类。在集合框中,ArrayList 和 LinkedList 都实现了List接口。

2. ArrayList 与 顺序表

2.1 线性表

线性表 (linear list)是 n 个具有 相同特性 的数据元素的有序序列。

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

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

2.2 顺序表

1.什么是顺序表

顺序表是用一段 物理地址连续的存储单元 依次存储数据元素的线性结构

2.为什么要有顺序表?

数组本身自带的方法无法满足需求。

需要我们自己定义一个类,让类来提供方法,帮助操作数组。

3.自己实现一个add方法

画图:

代码:

java 复制代码
public void add(int data) {
        if (isFull()) {
            grow();
        }
        this.array[this.usedSize] = data;
        this.usedSize++;
    }

    private void grow() {
        this.array = Arrays.copyOf(this.array, 2*this.array.length)
    }

    public boolean isFull() {
        return this.usedSize == array.length;
    }

逻辑非常严谨。

所以感觉很简单的功能,要写很多代码来保证严谨性。

2.3 ArrayList简介

在集合框架中,ArrayList是一个普通的类,实现了List接口,具体框架图如下:

【说明】

泛型:

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

接口:

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

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

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

并发:

和Vector不同,ArrayList不是线程安全的,在单线程下可以使用,在多线程中可以选择Vector或者CopyOnWriteArrayList

底层:

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

2.4 ArrayList使用

1. ArrayList的构造

java 复制代码
public static void main(String[] args) {
   // ArrayList创建
   
   // 构造一个空的列表
   List<Integer> list1 = new ArrayList<>();
   
   // 构造一个具有10个容量的列表
   List<Integer> list2 = new ArrayList<>(10);
   list2.add(1);
   list2.add(2);
   list2.add(3);

   // 利用 list2 构造 list3
   List<Integer> list3 = new ArrayList<>(list2);

}
1.1 ArrayList 基础属性的含义
1.2 ArrayList() 解析

此处涉及到一部分扩容,仔细理解,扩容剩余部分在扩容部分进行讲解.

1.3 ArrayList(int initialCapacity) 解析
1.4 ArrayList(Collection<? extends E> c) 解析

2. ArrayList 的常见操作

2.1 add(int index, E element)
2.2 remove的区分

此处红线原因:

因为这个操作过时了,但是可以用就是用的很少。

2.3 subList 存在问题
java 复制代码
public static void main(String[] args) {
    ArrayList<Integer> test = new ArrayList<>();
    test.add(1);
    test.add(2);
    test.add(3);
    test.add(4);
    test.add(5);
    System.out.println(test);
    List<Integer> list = test.subList(1, 3);
    System.out.println(list);
}

[1,3)

输出结果:

此时对裁剪后的数组进行改变,会出现与预期不符合的效果.

java 复制代码
    public static void main3(String[] args) {
        ArrayList<Integer> test = new ArrayList<>();
        test.add(1);
        test.add(2);
        test.add(3);
        test.add(4);
        test.add(5);
        System.out.println(test);

        List<Integer> list = test.subList(1, 3);
        System.out.println(list);

        System.out.println("======================================");

        list.set(0, 99);
        System.out.println(test); // 预期 1 2 3 4 5
        System.out.println(list); // 预期 99 3
    }

结果:

原因(如下图表示和代码分析):直接引用并没有创建新的对象

3. ArrayList的遍历

下面代码介绍了 ArrayList 的四种遍历方式

java 复制代码
public static void main(String[] args) {
        ArrayList<Integer> test = new ArrayList<>();
        test.add(1);
        test.add(2);
        test.add(3);
        test.add(4);
        test.add(5);
        System.out.println(test);

        System.out.println("===使用迭代器输出 listIterator 拓展功能===");
        ListIterator<Integer> iterator3 = test.listIterator(test.size());
        while (iterator3.hasPrevious()) {
            System.out.print(iterator3.previous() + " ");
        }
        System.out.println();

        System.out.println("===使用迭代器输出 iterator===");
        Iterator<Integer> iterator = test.iterator();
        while (iterator.hasNext()) {
            System.out.print(iterator.next() + " ");
        }
        System.out.println();

        System.out.println("===使用迭代器输出 listIterator===");
        ListIterator<Integer> iterator2 = test.listIterator();
        while (iterator2.hasNext()) {
            System.out.print(iterator2.next() + " ");
        }
        System.out.println();

        int size = test.size();
        System.out.println("===for循环输出===");
        for (int i = 0; i < size; i++) {
            System.out.print(test.get(i) + " ");
        }
        System.out.println();

        System.out.println("===for each循环输出===");
        for (Integer x : test) {
            System.out.print(x + " ");
        }
        System.out.println();
}

4. ArrayList的扩容

总结:

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

  2. 预估需要库容的大小初步预估按照1.5倍大小扩容如果用户所需大小超过预估1.5倍大小,则按照用户所需大小扩容,真正扩容之前检测是否能扩容成功,防止太大导致扩容失败

  3. 使用copyOf进行扩容

具体解析如下图:

5.ArrayList的练习

5.1 用于字符串

代码:

为什么 str2.contains(ch + "") 要 + ""

因为contains的参数是 CharSequence 而不是 Character

5.2 杨辉三角:
java 复制代码
public List<List<Integer>> generate(int numRows) {
    List<List<Integer>> ret = new ArrayList<>();

​    List<Integer> list0 = new ArrayList<>();
    list0.add(1);
    ret.add(list0);

​    // 从第二行开始 进行求每个元素
    for (int i = 1; i < numRows; i++) {
        // 处理第一个元素
        List<Integer> curRow = new ArrayList<>();
        curRow.add(1);

​        // 中间
        List<Integer> preRow = new ArrayList<>();
        preRow = ret.get(i - 1);
        for (int j = 1; j < i; j++) {
            int val1 = preRow.get(j);
            int val2 = preRow.get(j - 1);
            curRow.add(val1 + val2);
        }

​        // 尾巴
        curRow.add(1);
        ret.add(curRow);
}
5.3 洗牌算法

买牌:

java 复制代码
public List<Cart> buyCard() {
    List<Cart> cartList = new ArrayList<>();
    for (int i = 1; i <= 13; i++) {
        for (int j = 0; j < 4; j++) {
            int rank = i;
            String suit = suits[j];
            Cart cart = new Cart(suit, rank);
            cartList.add(cart);
        }
    }
    return cartList;
}

洗牌:

java 复制代码
public void shuffle(List<Cart> cartList) {
    Random random = new Random();
    for (int i = cartList.size() - 1; i > 0; i--) {
        int index = random.nextInt(cartList.size());
        swap(cartList, i, index);
    }
}

private void swap(List<Cart> cartList, int i, int j) {
    Cart tmp = cartList.get(i);
    cartList.set(i, cartList.get(j));
    cartList.set(j, tmp);
}

玩牌:

java 复制代码
public List<List<Cart>> play(List<Cart> cartList) {
    List<Cart> hand0 = new ArrayList<>();
    List<Cart> hand1 = new ArrayList<>();
    List<Cart> hand2 = new ArrayList<>();

​    List<List<Cart>> hand = new ArrayList<>();
    hand.add(hand0);
    hand.add(hand1);
    hand.add(hand2);

​    for (int i = 0; i < 5; i++) {
        for (int j = 0; j < 3; j++) {
            Cart cart = cartList.remove(0);
            hand.get(j).add(cart);
        }
    }

​    return hand;
}

2.5. ArrayList的缺点

  1. ArrayList底层使用连续的空间,任意位置插入或删除元素时,需要将该位置后序元素整体往前或者往后搬移,故时间复杂度为O(N)

  2. 扩容 需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。

  3. 扩容一般是呈 1.5 倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。

相关推荐
在未来等你11 分钟前
互联网大厂Java求职面试:AI大模型融合下的企业知识库架构设计与性能优化
java·向量数据库·ai大模型·spring ai·语义缓存·rag系统·多模态处理
寻星探路17 分钟前
JAVA与C语言之间的差异(一)
java·开发语言
tqs_1234528 分钟前
IntelliJ IDEA 中进行背景设置
java·ide·intellij-idea
爱上语文1 小时前
MyBatisPlus(1):快速入门
java·开发语言·数据库·后端·mybatis
异常君1 小时前
Java 与 Go 语言协作开发实战指南
java·go
似水এ᭄往昔1 小时前
【数据结构】--二叉树--堆(上)
数据结构·算法
信徒_1 小时前
Cloudflare
java
2501_915373882 小时前
Java调用C++教程:JNI与JNA两种方式详解
java·开发语言·c++
代码的余温2 小时前
Marshalling与Demarshalling深度解析
java·分布式·跨进程通信
好学且牛逼的马2 小时前
#13 学习反馈
java·学习·ai编程