【数据结构】ArrayList与顺序表

目录

什么是顺序表

实现一个简单的顺序表

•实现顺序表的属性

•实现顺序表的构造方法

•实现获取顺序表长度的方法size()

•实现在数组最后新增元素的方法add()

•实现在指定位置新增元素的方法add()

•实现判定是否包含某个元素的方法contains()

•实现查找某个元素对应的位置的方法indexOf()

•实现获取指定位置元素的方法get()

•实现给指定位置的元素设值的方法set()

•实现删除第一次出现的某个数字的方法remove()

•实现清空数组的方法clear()

ArrayList

什么是ArrayList

ArrayList的构造

ArrayList的常见操作方法

ArrayList的遍历

ArrayList的扩容机制


什么是顺序表

顺序表是用一段 物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储,在数组上完成数据的增删查改。顺序表是线性表(由n个具有相同特性的数据元素组成的有限序列,物理地址不一定连续) 的一种。

实现一个简单的顺序表

•实现顺序表的属性

要想实现一个顺序表的类,首先需要确定它的属性,其中 elem 是存储数据的数组,usedSide 是数组中有效数字的个数,DEFAULT_SIZE 则是表示数组的初始大小:
public class MyArrayList {
    private int[] elem;//被操作的数组
    private int usedSize;//记录数组中有效数字的个数
    private static final int DEFAULT_SIZE = 10;//数组的初始大小
}

•实现顺序表的构造方法

一个类除了属性外还得有它的构造方法,我们可以在构造方法中对数组 elem进行初始化:

    public MyArrayList() {
        this.elem = new int[DEFAULT_SIZE];
    }

初始化好数组后就可以实现操作数组 elem 的方法了,就从获取顺序表的长度开始吧~

•实现获取顺序表长度的方法size()

因为 usedSize 记录了数组中有效数字的个数,故直接返回usedSize即可获取顺序表长度:

    // 获取顺序表长度
    public int size() {
        return this.usedSize;
    }

•实现在数组最后新增元素的方法add()

接着实现能在数组后添加元素的方法,添加元素时需要注意,➀.如果当前数组满了,则需要扩容后再添加,➂.添加元素后记录有效数字的 usedSide 需要加一:

    // 新增元素,默认在数组最后新增
    public void add(int data) {
        //判断当前数组是否满了
        if (isFull()) {
            //满了就需要扩容
            this.elem = Arrays.copyOf(this.elem, 2*this.elem.length);
        }
        //添加新元素
        this.elem[usedSize] = data;
        //添加后有效数字加一
        usedSize++;
    }
    //判断数组是否放满了
    public boolean isFull() {
        return size() >= this.elem.length;
    }

•实现在指定位置新增元素的方法add()

➀在指定位置新增元素同样需要判断数组是否满了,满了需要先扩容,➁.然后还要判断插入的位置是否合法,不合法抛出自定义的异常,➂.再将指定位置后面的元素整体往后挪出一个位置,➃.将新元素插入到指定位置,➄.最后不用忘记有效数字要加一:

  // 在 pos 位置新增元素
    public void add(int pos, int data)  throws PosWrongfulException {
        //判断当前数组是否满了
        if (isFull()) {
            //满了就需要扩容
            this.elem = Arrays.copyOf(this.elem, 2*this.elem.length);
        }
        //判断 pos 位置是否合法
        if (pos < 0 || pos > this.usedSize) {
            throw new PosWrongfulException("add():pos 位置不合法!");
        }
        //挪动数据
        for (int i = usedSize-1; i >= pos; i--) {
            this.elem[i+1] = this.elem[i];
        }
        //插入数据
        this.elem[pos] = data;
        //插入后有效数字加一
        usedSize++;
    }

自定义异常类PosWrongfulException:

public class PosWrongfulException extends RuntimeException{
    public PosWrongfulException() {

    }
    public PosWrongfulException(String message) {
        super(message);
    }
}

•实现判定是否包含某个元素的方法contains()

判定是否包含某个元素直接遍历一遍数组即可:

// 判定是否包含某个元素
    public boolean contains(int toFind){
        for (int i = 0; i < this.size(); i++) {
            if (this.elem[i] == toFind) {
                return true;
            }
        }
        return false;
    }

•实现查找某个元素对应的位置的方法indexOf()

同样遍历一遍数组即可:

    // 查找某个元素对应的位置
    public int indexOf(int toFind) {
        for (int i = 0; i < this.size(); i++) {
            if (this.elem[i] == toFind) {
                return i;
            }
        }
        return -1;
    }

•实现获取指定位置元素的方法get()

要获取指定位置的元素需要➀.判断当前数组是否为空,➁.指定的位置是否合法,➂.最后再返回指定位置的元素

    // 获取 pos 位置的元素
    public int get(int pos) {
        //判断数组是否为空
        if (isEmpty()) {
            throw new EmptyException("get():当前顺序表为空!");
        }
        //判断 pos 位置是否合法
        if (pos < 0 || pos >= size()) {
            throw new PosWrongfulException("get():pos 位置不合法!");
        }
        //返回 pos 位置元素
        return this.elem[pos];
    }
    //判断当前数组是否为空
    public boolean isEmpty() {
        return size() == 0;
    }

自定义异常类EmptyException:

public class EmptyException extends RuntimeException {
    public EmptyException() {

    }
    public EmptyException(String message) {
        super(message);
    }
}

•实现给指定位置的元素设值的方法set()

需要➀.先判断当前数组是否为空,➁.指定的位置是否合法,➂再更新指定位置的元素的值

// 给 pos 位置的元素设为 value
    public void set(int pos, int value) {
        //判断数组是否为空
        if (isEmpty()) {
            throw new EmptyException("set():当前顺序表为空!");
        }
        //判断 pos 位置是否合法
        if (pos < 0 || pos >= size()) {
            throw new PosWrongfulException("set():pos 位置不合法!");
        }
        //更新 pos 位置元素的值
        this.elem[pos] = value;
    }

•实现删除第一次出现的某个数字的方法remove()

要想删除数组中首次出现的某个数字得➀.先保证当前数组不为空,➁.再去寻找该数字的位置,➂.判断是否找的到,找不到抛出异常,找到了将指定位置后面的元素一起往前挪一个位置,➃.最后不要忘记记录有效数字个数的属性值减一

//删除第一次出现的关键字key
    public void remove(int key) {
        //判断当前数组是否为空
        if (isEmpty()) {
            throw new EmptyException("remove():当前顺序表为空!");
        }
        //找到第一次出现的数字key
        int index = this.indexOf(key);
        //判断是否找到第一次出现的数字key
        if (index == -1) {
            throw new NoFoundException6("remove:找不到关键字 key");
        }
        //将后面的元素逐个往前挪
        for (int i = index; i < size()-1; i++) {
            this.elem[i] = this.elem[i+1];
        }
        //删掉一个元素usedSize减一
        usedSize--;
    }

自定义异常类NoFoundException:

public class NoFoundException6 extends RuntimeException {
    public NoFoundException6() {

    }
    public NoFoundException6(String message) {
        super(message);
    }
}

最后来个简单的:

•实现清空数组的方法clear()

因为是整形数组,所以直接令有效数字为零就好了

    // 清空顺序表
    public void clear() {
        this.usedSize = 0;
    }

注意,若数组存放的是引用类型的话还需要将每个位置的内存给释放掉。

ArrayList

通过上面自己实现的 MyArrayList 类我们对 ArrayList 就有了基本的了解,接下来就一起来揭开Java集合框架中 ArrayList 的面纱~

什么是ArrayList

ArrayList 类是一个可以动态修改的数组,是一个动态类型的顺序表,它是在 java.util 包里面的,ArrayList实现了 list,RandomAccess,Serializable,Cloneable 接口:

• ArrayList 实现了 List 接口,表明 ArrayList 是一个线性表

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

 ArrayList 实现了  Serializable  接口,表明  ArrayList  是支持序列化的
 ArrayList 实现了  Cloneable  接口,表明  ArrayList  是可以克隆

ArrayList的构造

ArrayList 有三个构造方法:

•ArrayList():构造一个初始容量为10的空列表

ArrayList<Integer> arrayList = new ArrayList<>();

注:在ArrayList()的构造方法里并没有为数组分配内存,只有当第一次添加数组元素时才会为数组分配大小为10的内存空间

•ArrayList(Collection<? extends E> c):构造一个包含指定集合的元素的构造方法。(只要构造方法里传的是实现了Collection接口的,且其泛型类型是E的子类或其本身的都可以)

ArrayList<Integer> arrayList = new ArrayList<>();
ArrayList<Integer> arrayList1 = new ArrayList<>(arrayList);

•ArrayList(int initialCapacity):构造具有指定初始容量的空列表

ArrayList<Integer> arrayList = new ArrayList<>(10);

ArrayList的常见操作方法

方法描述
boolean add (E e)
尾插 e
void add (int index, E element)
e 插入到 index 位置
boolean addAl l (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 i ndexOf (Object o)
返回第一个 o 所在下标
int l astIndexOf (Object o)
返回最后一个 o 的下标
List<E> subList (int fromIndex, int toIndex)
截取部分 list
import java.util.ArrayList;
import java.util.List;
public class Test {
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        ArrayList<Integer> arrayList1 = new ArrayList<>(arrayList);
        //尾插 e
        arrayList.add(1);
        arrayList.add(2);
        arrayList.add(3);
        //将 e 插入到 index 位置
        arrayList.add(0,2);
        //尾插c中的元素
        arrayList1.addAll(arrayList);
        //删除 index 位置元素
        arrayList.remove(0);
        //删除遇到的第一个 o
        arrayList.remove(new Integer(2));
        //获取下标 index 位置元素
        int val = arrayList.get(0);
        //将下标 index 位置元素设置为 element
        arrayList.set(0, 3);
        //判断 o 是否在线性表中
        boolean x = arrayList.contains(3);
        //返回第一个 o 所在下标
        int c = arrayList.indexOf(new Integer(3));
        //返回最后一个 o 的下标
        int d = arrayList.lastIndexOf(new Integer(3));
        //截取部分 list
        List<Integer> list = arrayList.subList(0, 2);
        //获取list中有效元素个数
        int n = arrayList.size();
        //清空
        arrayList.clear();
    }
}

注:subList()截取的部分 list 与原本的共用一块内存空间

ArrayList的遍历

ArrayList 可以使用四种方式遍历:直接输出、for循环+下标、foreach、使用迭代器

•直接输出:

import java.util.ArrayList;
public class Test {
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        arrayList.add(1);
        arrayList.add(2);
        arrayList.add(3);
        System.out.println(arrayList);
    }
}

运行结果:

•for循环:

import java.util.ArrayList;
public class Test {
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        arrayList.add(1);
        arrayList.add(2);
        arrayList.add(3);
        for (int i = 0; i < arrayList.size(); i++) {
            System.out.print(arrayList.get(i) + " ");
        }
        System.out.println();
    }
}

运行结果:

•foreach:

import java.util.ArrayList;
public class Test {
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        arrayList.add(1);
        arrayList.add(2);
        arrayList.add(3);
        for (Integer a : arrayList) {
            System.out.print(a + " ");
        }
        System.out.println();
    }
}

运行结果:

•使用迭代器:

import java.util.ArrayList;
public class Test {
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        arrayList.add(1);
        arrayList.add(2);
        arrayList.add(3);
        Iterator<Integer> it = arrayList.iterator();
        while(it.hasNext()) {
            System.out.print(it.next() + " ");
        }
        System.out.println();
    }
}

运行结果:

ArrayList的扩容机制

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

2. 预估需要扩容的大小,初步预估按照1.5倍大小扩容 ,如果用户所需大小超过预估1.5倍大小,则按照用户所需大小扩容

3. 真正扩容之前检测是否能扩容成功,防止太大导致扩容失败
4. 能扩容成功则 使用 copyOf 进行扩容
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

记得开心一点啊

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

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

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

打赏作者

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

抵扣说明:

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

余额充值