【数据结构与算法】模拟实现ArrayList

简介: ArrayList 类是一个可以动态修改的数组,与普通数组的区别就是它是没有固定大小的限制,我们可以添加或删除元素。ArrayList 继承了 AbstractList ,并实现了 List 接口。

ArrayList简介


ArrayList 类是一个可以动态修改的数组,与普通数组的区别就是它是没有固定大小的限制,我们可以添加或删除元素。ArrayList 继承了 AbstractList ,并实现了 List 接口。

简单来说ArrayList的底层就是一个动态的顺序表。

而顺序表的底层就是数组.

因此要模式实现ArrayList就相当于基于数组实现一些操作,例如增删查改等。


ArrayList模拟实现


public int[] elem;

   public int usedSize;//记录有效数据的个数

   //默认容量

   private static final int DEFAULT_SIZE = 10;

   public MyArraylist() {

       this.elem = new int[DEFAULT_SIZE];

   }

将数据放在elem这个数组中,usedSize用于计算有效数据的个数,DEFAULT_SIZE是elem的默认容量。


打印顺序表


打印顺序表,我们可以使用循环对顺序表中的数据进行打印。

   /**

    * 打印顺序表:

    * 根据usedSize判断即可

    */

   public void display() {

       for (int i = 0; i < this.usedSize; i++) {

           System.out.print(this.elem[i] + " ");

       }

       System.out.println();

   }

注意:顺序表中elem的有效数据是从[0,usedSize),不能直接将整个数组打印完,我们存放的数据不一定能将数组放满。


获取顺序表的长度


此处的顺序表长度并不是底层数组的长度,而是数组种有效数据的个数.因此我们直接返回usedSize即可

public int size() {

       return this.usedSize;

   }


判断当前的顺序表是不是满的


要想判断当前的顺序表是不是满的,只需要看看usedSize(有效数据的个数)是不是大于等与底层数组的长度 如果是满的,返回true.如果不是满的,就返回false

   /**

    * 判断当前的顺序表是不是满的!

    */

   public boolean isFull() {

       if (size() >= this.elem.length) {

           return true;

       }

       return false;

       // 更简单的写法

       // return size() >= this.elem.length;

   }


判断当前顺序表是不是空的


判断当前顺序表是不是空的,只要看顺序表的有效数据是不是等于0即可

    //判断顺序表是否为空

   private boolean isEmpty() {

       return size()==0;

   }


数组扩容


在实现增加元素之前,我们要考虑一下,当数组满了之后,应该怎么做.顺序表不是链表,不用考虑满不满的这种情况,但顺序表不一样,数组长度是我们事先定好的.

如果数组长度设置的过大,而数据量很少,势必会造成空间的浪费.设置的过小,可能无法把所有元素存放进去.

为了避免这种情况,我们就可以使用动态顺序表.虽然听上去很高大上,但其实也就是加了一个动态扩容的过程

扩容也并不复杂,使用Arrays.copyOf方法即可

数组名 = Arrays.copyOf(要拷贝的数组,拷贝数组的长度);

在拷贝时,如果要拷贝数组的长度超过了原数组,那么超过原数组的部分,就是数组类型的默认值


新增元素,默认在数组最后新增


在解决数组满了这种情况之后,新增元素就很简单了.

一共分为两步:首先先判断数组是否满了,数组满了,就进行扩容.然后进行新增元素.如果没满,直接增加元素即可

    /** 新增元素,默认在数组最后新增

    * 判断顺序表是否满了

    * 满了->扩容

    * @param data

    */

   public void add(int data) {

       if (isFull()) {

           //扩容

           this.elem = Arrays.copyOf(this.elem,2*this.elem.length);

       }

       this.elem[usedSize] = data;

       usedSize++;

   }


在指定pos位置新增元素


在指定位置新增元素,首先要判断数组是否满了,如果满了要进行扩容.其次,pos位置是否合法,合法位置才能新增元素.

因为是从指定位置新增元素,如果这个位置不是在最后面,那么在新增元素时,首先要把pos及其以后位置的元素都往后移动一位,将pos位置空出来,这样才能将元素放进去

    public void add(int pos, int data) {

       if(isFull()){

           this.elem = Arrays.copyOf(this.elem,2*this.elem.length);

       }

       if(checkPos(pos)){

           for (int i = usedSize; i >= pos; i--) {

               this.elem[i] = this.elem[i-1];

           }

           this.elem[pos] = data;

       }

       this.usedSize++;

   }

   // 检查pos位置是否合法

   private boolean checkPos(int pos) {

       if (pos < 0 || pos > size()) {

           System.out.println("pos位置不合法");

           return false;

       }

       return true;//合法

   }


查找某个元素对应的位置


与上面判断是否包含某个元素相同,遍历就行.

如果相等,返回当前的i值,如果不相等,就继续遍历.如果遍历完还没找到,就返回-1

   // 查找某个元素对应的位置

   public int indexOf(int toFind) {

       for (int i = 0; i < this.usedSize; i++) {

           if (toFind == this.elem[i]) {

               return i;

            }

       }

           return -1;

   }


获取指定pos位置的元素


先检查顺序表是否为空,如果是空的.直接返回-1.如果不为空,再检查pos位置是否合法.

用自定义异常解决了顺序表为空这种情况,处理方法不一,合理即可

   // 获取 pos 位置的元素

   public int get(int pos) {

       if(isEmpty()){

           throw new EmptyException("顺序表为空!");

       }

       if(checkPos(pos)){

           return this.elem[pos];

       }

       return -1;

   }


将指定pos位置的元素替换成value


先判断顺序表是否为空,在判断pos位置是否合法.满足即可将pos位置的值更新为value

  // 给 pos 位置的元素设为【更新为】 value

   public void set(int pos, int value) {

       if(isEmpty()){

           throw new EmptyException("顺序表为空!");

       }

       if(pos < 0 || pos > size()){

           System.out.println("pos位置不合法");

           return;

       }

       this.elem[pos] = value;

   }


删除第一次出现的关键字key


先判断顺序表是否为空,是空直接返回

然后判断顺序表中是否存在key,如果存在key,直接让key所在的位置的后面元素,一个一个往前覆盖,如果不存在,直接返回即可

 public void remove(int key) {

       if(isEmpty()){

           throw new EmptyException("顺序表为空!");

       }

       if(contains(key)) {

           int ret = indexOf(key);

           for (int i = ret; i < usedSize; i++) {

               this.elem[i] = this.elem[i+1];

           }

       }

       this.usedSize--;

   }


清空顺序表


public void clear() {

       this.usedSize = 0;

   }

这里的清空并不是真的清空,而是下次增加元素时,会把之前有的元素覆盖掉.如果不覆盖,那么原来的数据还是存在数组中的

如果想要彻底清空顺序表,就需要使用循环,将数组一个一个置为0


完整代码

主要部分:

import java.util.Arrays;

public class MyArrayList {

   public int[] elem;

   public int usedSize;//0//默认容量

   private static final int DEFAULT_SIZE = 10;

   public MyArrayList() {

       this.elem = new int[DEFAULT_SIZE];

   }

   /**

    * 打印顺序表:

    * 根据usedSize判断即可

    */

   public void display() {

       for (int i = 0; i < this.usedSize; i++) {

           System.out.print(this.elem[i] + " ");

       }

       System.out.println();

   }

   /** 新增元素,默认在数组最后新增

    * 判断顺序表是否满了

    * 满了->扩容

    * @param data

    */

   public void add(int data) {

       if (isFull()) {

           //扩容

           this.elem = Arrays.copyOf(this.elem,2*this.elem.length);

       }

       this.elem[usedSize] = data;

       usedSize++;

   }

   /**

    * 判断当前的顺序表是不是满的!

    * @return true:满   false代表空

    */

   public boolean isFull() {

//        if (size() >= this.elem.length) {

//            return true;

//        }

//        return false;

       return size() >= this.elem.length;

   }

   private boolean checkPos(int pos) {

       if (pos < 0 || pos > size()) {

           System.out.println("pos位置不合法");

           return false;

       }

       return true;//合法

   }

   /** 在 pos 位置新增元素

    * 方法:将pos位置的元素向后移动

    * 情况:

    *  pos位置不合法

    *  不能间隔

    * @param pos

    * @param data

    *

    */

   public void add(int pos, int data) {

       if(isFull()){

           this.elem = Arrays.copyOf(this.elem,2*this.elem.length);

       }

       if(checkPos(pos)){

           for (int i = usedSize; i >= pos; i--) {

               this.elem[i] = this.elem[i-1];

           }

           this.elem[pos] = data;

       }

       this.usedSize++;

   }

   // 判定是否包含某个元素

   public boolean contains(int toFind) {

       for (int i = 0; i < this.usedSize; i++) {

           if (toFind == this.elem[i]) {

               return true;

           } else {

               continue;

           }

       }

       return false;

   }

   // 查找某个元素对应的位置

   public int indexOf(int toFind) {

       for (int i = 0; i < this.usedSize; i++) {

           if (toFind == this.elem[i]) {

               return i;

            }

       }

           return -1;

   }

   // 获取 pos 位置的元素

   public int get(int pos) {

       if(isEmpty()){

           throw new EmptyException("顺序表为空!");

       }

       if(checkPos(pos)){

           return this.elem[pos];

       }

       return -1;

   }

   //判断顺序表是否为空

   private boolean isEmpty() {

       return size()==0;

   }

   // 给 pos 位置的元素设为【更新为】 value

   public void set(int pos, int value) {

       if(isEmpty()){

           throw new EmptyException("顺序表为空!");

       }

       if(pos < 0 || pos > size()){

           System.out.println("pos位置不合法");

           return;

       }

       this.elem[pos] = value;

   }

   /**

    * 删除第一次出现的关键字key

    * @param key

    */

   public void remove(int key) {

       if(isEmpty()){

           throw new EmptyException("顺序表为空!");

       }

       if(contains(key)) {

           int ret = indexOf(key);

           for (int i = ret; i < usedSize; i++) {

               this.elem[i] = this.elem[i+1];

           }

       }

       this.usedSize--;

   }

   // 获取顺序表长度

   public int size() {

       return this.usedSize;

   }

   // 清空顺序表

   public void clear() {

       this.usedSize = 0;

   }

}

自定义异常部分

public class EmptyException extends RuntimeException{

   public EmptyException() {

   }

   public EmptyException(String message) {

       super(message);

   }

}

相关文章
|
5月前
|
Java
【Java集合类面试二十六】、介绍一下ArrayList的数据结构?
ArrayList是基于可动态扩展的数组实现的,支持快速随机访问,但在插入和删除操作时可能需要数组复制而性能较差。
|
2月前
|
存储 Java 索引
Java中的数据结构:ArrayList和LinkedList的比较
【10月更文挑战第28天】在Java编程世界中,数据结构是构建复杂程序的基石。本文将深入探讨两种常用的数据结构:ArrayList和LinkedList,通过直观的比喻和实例分析,揭示它们各自的优势与局限,帮助你在面对不同的编程挑战时做出明智的选择。
|
3月前
|
Java C++ 索引
让星星⭐月亮告诉你,LinkedList和ArrayList底层数据结构及方法源码说明
`LinkedList` 和 `ArrayList` 是 Java 中两种常见的列表实现。`LinkedList` 基于双向链表,适合频繁的插入和删除操作,但按索引访问元素效率较低。`ArrayList` 基于动态数组,支持快速随机访问,但在中间位置插入或删除元素时性能较差。两者均实现了 `List` 接口,`LinkedList` 还额外实现了 `Deque` 接口,提供了更多队列操作。
32 3
|
3月前
|
存储 缓存 索引
从底层数据结构和CPU缓存两方面剖析LinkedList的查询效率为什么比ArrayList低
本文详细对比了ArrayList和LinkedList的查询效率,从底层数据结构和CPU缓存两个方面进行分析。ArrayList基于动态数组,支持随机访问,查询时间复杂度为O(1),且CPU缓存对其友好;而LinkedList基于双向链表,需要逐个节点遍历,查询时间复杂度为O(n),且CPU缓存对其帮助不大。文章还探讨了CPU缓存对数组增删操作的影响,指出缓存主要作用于读取而非修改。通过这些分析,加深了对这两种数据结构的理解。
53 2
|
3月前
|
设计模式 安全 容器
数据结构第一篇【探究List和ArrayList之间的奥秘 】
数据结构第一篇【探究List和ArrayList之间的奥秘 】
29 5
|
4月前
|
存储 Java 程序员
【数据结构】初识集合&深入剖析顺序表(Arraylist)
Java集合框架主要由接口、实现类及迭代器组成,包括Collection和Map两大类。Collection涵盖List(有序、可重复)、Set(无序、不可重复),Map则由键值对构成。集合通过接口定义基本操作,具体实现由各类如ArrayList、HashSet等提供。迭代器允许遍历集合而不暴露其实现细节。List系列集合元素有序且可重复,Set系列元素无序且不可重复。集合遍历可通过迭代器、增强for循环、普通for循环及Lambda表达式实现,各有适用场景。其中ArrayList实现了动态数组功能,可根据需求自动调整大小。
43 11
|
3月前
|
算法 Java 测试技术
数据结构 —— Java自定义代码实现顺序表,包含测试用例以及ArrayList的使用以及相关算法题
文章详细介绍了如何用Java自定义实现一个顺序表类,包括插入、删除、获取数据元素、求数据个数等功能,并对顺序表进行了测试,最后还提及了Java中自带的顺序表实现类ArrayList。
43 0
|
7月前
|
存储 Java API
Java数据结构之ArrayList(如果想知道Java中有关ArrayList的知识点,那么只看这一篇就足够了!)
Java数据结构之ArrayList(如果想知道Java中有关ArrayList的知识点,那么只看这一篇就足够了!)
Java数据结构之ArrayList(如果想知道Java中有关ArrayList的知识点,那么只看这一篇就足够了!)
|
7月前
|
存储 Java 索引
实现一个类似ArrayList的数据结构
实现一个类似ArrayList的数据结构
59 4
|
7月前
|
存储 Java 索引
【Java】LinkedList vs. ArrayList:Java中的数据结构选择
【Java】LinkedList vs. ArrayList:Java中的数据结构选择
36 3