《数据结构》顺序表ArrayList

《数据结构》顺序表ArrayList

什么是顺序表:

顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储,在数组上完成数据的增删查改。

顺序表通过控制数组中的有效数据个数,进而包装成一个连续存储数据元素的线性结构.

模拟实现顺序表(以int类型为例)

import java.util.*;
class MyArraylist{
    public int size;
    public int[] elem;
    MyArraylist(){
        elem = new int[0];
    }
    MyArraylist(int n){
        elem = new int[n];
    }
    private boolean isfull(){
        return size==elem.length;
    }
    private boolean checkpos(int pos){
        if(pos<0||pos>=size){
            return false;
        }
        return true;
    }
    public boolean isempty(){
        return size==0;
    }
    public void display(){
        for(int i = 0;i < size;i++){
            System.out.print(elem[i]+" ");
        }
    }

    public void add(int x){
        if(isempty()){
            elem = new int[10];
        }
        if(isfull()){
            elem = Arrays.copyOf(elem,elem.length*2);
        }
        elem[size] = x;
        size++;
    }
    public void add(int x,int pos){
        if(isempty()){
            elem = new int[10];
        }
        if(isfull()){
            elem = Arrays.copyOf(elem,elem.length*2);
        }
        if(checkpos(pos)){
            for(int i = size;i>pos;i--){
                elem[i] = elem[i-1];
            }
            elem[pos] = x;
            size++;
        }else{
            System.out.println("pos下标不合法");
        }
    }
    public boolean contains(int tofind){
        if(isempty()){
            System.out.println("顺序表为空,查找失败");
        }
        for(int i = 0;i < size;i++){
            if(elem[i]==tofind){
                return true;
            }
        }
        return false;
    }
    //返回数值k的下标
    public int indexOf(int k){
        if(isempty()){
            System.out.println("顺序表为空,查找失败");
        }
        for(int i = 0;i < size;i++){
            if(elem[i]==k){
                return i;
            }
        }
        return -1;
    }
    //得到pos下标的值
    public int get(int pos){
        if(!checkpos(pos)){
            System.out.println("pos下标不合法");
        }
        return elem[pos];
    }
    public void set(int x,int pos){
        if(!checkpos(pos)){
            System.out.println("pos下标不合法");
        }else{
            elem[pos] = x;
        }
    }
    public void remove(int k){
        if(!checkpos(k)){
           System.out.println("pos下标不合法");
        }else{
            for(int i = k;i<size;i++){
               elem[i] = elem[i+1];
            }
            size--;
        }
    }
    public int size(){
        return size;
    }
    public void clean(){
        size = 0;
    }
}
public class Main{
    public static void main(String[] args){
        MyArraylist arraylist = new MyArraylist();
        arraylist.add(1);
        arraylist.add(2);
        arraylist.add(1);
        arraylist.add(2);
        arraylist.add(1);
        arraylist.add(2);
        //arraylist.display();
        arraylist.add(3,2);
        arraylist.display();
        arraylist.remove(2);
        arraylist.display();
    }
}

值得注意的是:

在我们的remove和clean方法中,如果删除的元素是引用类型时,需要把这个要被删除的元素置为null,防止出现内存泄露的问题.一般而言,对于pos指针不合法,或者顺序表为空无法查询的情况,我们也可以通过抛出异常来终止本次操作并提醒程序员.

详解ArrayList:

ArrayList实现的接口:

在这里插入图片描述

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

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

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

  4. ArrayList不是线程安全的,可以在单线程使用.

ArrayList的构造方法:
  • ArrayList()

  • ArrayList(Collection<? extends E> c) 利用其他 Collection 构建 ArrayList

  • ArrayList(int initialCapacity) 指定顺序表初始容量

对源码的解析:

对于ArrayList():

在这里插入图片描述

在这里插入图片描述

我们可以很明显的得出,使用ArrayList构造出来的顺序表,底层是一个空数组,没有容量.

对于ArrayList(Collection<? extends E> c):

在这里插入图片描述

首先.这个构造方法会将接受的c转化为数组存入elem中,并且如果该数组的大小为空,则直接生成一个空数组来作为底层数组,如果不为空 这会判断这个数组是不是一个Object数组,如果是一个Object数组,则不需要进行拷贝了,已经赋值成功了,如果不是一个Object数组,则需要进行拷贝.

对于ArrayList(int initialCapacity)
在这里插入图片描述

作用比较单一 ,传入的int是多大,那么底层数组的容量就是多大.

ArrayList的方法

方法解释
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 subList(int fromIndex, int toIndex)截取部分 list

ArrayList的扩容机制

为了ArrayList的动态存储,对于数组的长度采用的动态扩容的机制.

源码

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

重点关注grow()

private void grow(int minCapacity) {
// 获取旧空间大小
    int oldCapacity = elementData.length;
// 预计按照1.5倍方式扩容
    int newCapacity = oldCapacity + (oldCapacity >> 1);
// 如果用户需要扩容大小 超过 原空间1.5倍,按照用户所需大小扩容
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
// 如果需要扩容大小超过MAX_ARRAY_SIZE,重新计算容量大小
     if (newCapacity - MAX_ARRAY_SIZE > 0)
         newCapacity = hugeCapacity(minCapacity);
// 调用copyOf扩容
     elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
// 如果minCapacity小于0,抛出OutOfMemoryError异常
     if (minCapacity < 0)
          throw new OutOfMemoryError();
     return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}

在确保数组已经无法存下元素之后,并且用户需要扩容的数值没有问题的情况下,grow保证了ArrayList的动态扩容机制

  • 初步预估按照1.5倍大小扩容

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

  • 真正扩容之前检测是否能扩容成功,防止太大导致扩容失败

实现了ArrayList 的1.5倍扩容

ArrayList的缺陷

  • 顺序表中间/头部的插入删除,时间复杂度为O(N)

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
数据结构中的顺序表,也称为数组或线性表,是一种最简单的数据存储形式,其中元素按照它们在内存中的连续位置(索引)进行排列。每个元素都有一个唯一的标识符(通常是整数),可以通过这个标识符直接访问其对应的值。 在大多数编程语言中,你可以用数组来实现顺序表。例如,在C++中,定义一个动态大小的顺序列表(栈或队列)可能如下所示: ```cpp template <typename T> class ArrayList { private: T* data; // 存储元素的指针 int size; // 当前元素的数量 int capacity; // 容量,即数组的最大大小 public: ArrayList(int initialCapacity = 10) : size(0), capacity(initialCapacity) { data = new T[capacity]; } // 添加元素 void push_back(const T& value) { if (size == capacity) { resize(capacity * 2); // 如果满了,扩展容量 } data[size++] = value; } // 删除元素 void pop_back() { if (size > 0) { size--; } } // 访问元素 const T& at(int index) const { if (index >= 0 && index < size) { return data[index]; } else { throw std::out_of_range("Index out of range"); } } // 其他方法... private: // 扩展数组 void resize(int newCapacity) { T* newData = new T[newCapacity]; for (int i = 0; i < size; i++) { newData[i] = data[i]; } delete[] data; data = newData; capacity = newCapacity; } }; ``` 这是一个基础的顺序表实现,提供了添加、删除和访问元素的功能。实际使用时,可能会根据需求添加更多的方法和优化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小连~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值