手工实现ArrayList,包含自定义实现ArrayList和ArrayList测试类

目录

1、核心原理:数组的动态扩容,当一个数组容量不够的时候,创建一个新的数组,将旧的数组的内容复制到新的数组上。

2、主要实现的功能:元素的增加,删除,修改,查看、判空

3、具体细节:

3.0 定义的一些常量代码段如下:

3.1构造器,两个,一个有参构造,一个无参构造代码段如下:

3.2 add方法:有两种重载形式:一种是在末尾追加,另一种在任意位置添加(区间在size范围内)同时在添加的时候应做两个检查,首先要检查传入的索引是否合法,另一个保证容量足够(不够将进行扩容)

3.3元素的更新操作,在此提供了两种更新的操作,一种是根据元素元素来操作,一种是根据索引来操作

3.4元素的删除操作,两种方式,一种是根据索引来删除(要检查索引的合法性),一种是根据元素来删除。

3.5根据索引查看元素

3.6ArrayList判空,思路为:判定其size是否为0,代码如下

3.7清空元素,思路,仅需把有所有的元素置为null和size=0即可。代码块如下:

3.8 toString()方法

4、测试用例

 5、完整源代码

6、时间复杂度分析


1、核心原理:数组的动态扩容,当一个数组容量不够的时候,创建一个新的数组,将旧的数组的内容复制到新的数组上。

2、主要实现的功能:元素的增加,删除,修改,查看、判空

3、具体细节:

3.0 定义的一些常量代码段如下:


    /**
     * description 元素的个数
     */
    private int size;

    /**
     * description 泛型类数组
     */
    private E[] elements;

    /**
     * description 定义初始化默认容量为10
     */
    private static final int DEFAULT_INITIAL_CAPACITY = 10;

    /**
     * description 元素未找到抛出-1
     */
    private static final int ELEMENT_NOT_FOUND = -1;

    /**
     * description 元素不存在
     */
    private final E ELEMENT_NOT_DOES_EXIST = null;

3.1构造器,两个,一个有参构造,一个无参构造代码段如下:


    /**
     * description 有参构造器,传入的参数小于小于默认值时采用默认值,否则采用传入的值
     *
     * @param capacity 传入的容量值
     */
    public MyArrayList(int capacity) {
        if (capacity >= DEFAULT_INITIAL_CAPACITY) {
            elements = (E[]) new Object[capacity];
        } else {
            elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY];
        }
    }

    /**
     * description 无参构造器,采用默认值
     */
    public MyArrayList() {
        this(DEFAULT_INITIAL_CAPACITY);
    }

3.2 add方法:有两种重载形式:一种是在末尾追加,另一种在任意位置添加(区间在size范围内)同时在添加的时候应做两个检查,首先要检查传入的索引是否合法,另一个保证容量足够(不够将进行扩容)

检查索引是否合法的方法:


    /**
     * description 检查是否越界
     *
     * @param index 待检测的索引
     */
    private void isOutOfBoundsForAdd(int index) throws IllegalAccessException {
        if (index <= size && index >= 0) return;
        throw new IllegalAccessException("传入的索引越界");
    }

保证容量足够的方法:扩容采用位运算,加快了运算速度,不知道位运算为何物的可参考我的另一篇博客

    /**
     * description 保证容量足够
     */
    private void ensureCapacity() {
        if (size < elements.length) return;
        /*
         * elements.length >> 1,位运算,相当于除以二,加快运算速度
         * 等价于 int C = elements.length *1.5;
         */
        int C = elements.length + (elements.length >> 1);
        E[] newElements = (E[]) new Object[C];
        //数组复制
        for (int i = 0; i < elements.length; i++) {
            newElements[i] = elements[i];
        }
        elements = newElements;
        System.out.println("已扩容,新容量为:" + elements.length);
    }

在任意位置添加的方法


    /**
     * description 从指定地方添加元素
     *
     * @param index   指定的索引
     * @param element 传入的元素
     * @throws IllegalAccessException 传入的索引越界异常
     */
    public void add(int index, E element) throws IllegalAccessException {
        isOutOfBoundsForAdd(index);
        ensureCapacity();
        for (int i = size; i > index; i--) {
            elements[i] = elements[i - 1];
        }
        elements[index] = element;
        size++;
    }

在末尾追加的时候只需要调用add(int index, E element)方法即可

代码块如下:


    /**
     * description 添加元素的方法,默认从末尾追加
     *
     * @param element 传入一个添加的元素
     */
    public void add(E element) throws IllegalAccessException {
        add(size, element);
    }

3.3元素的更新操作,在此提供了两种更新的操作,一种是根据元素元素来操作,一种是根据索引来操作

思路,不管是根据元素还是索引,最后都是将对应索引元素进行替换,因此,我们先给出根据索引更新元素的值的方法,同时他应该告诉外界我们更新的元素,这样别人在调用我们的接口的时候会更加方便,注意判定索引是否合法


    /**
     * description 更新指定索引的元素
     *
     * @param index   需要更新元素的索引的值
     * @param element 传入的更新元素
     * @return 更新前的元素
     */
    public E update(int index, E element) {
        if(index>size||index<0)  return ELEMENT_NOT_DOES_EXIST;
        E e = elements[index];
        elements[index] = element;
        return e;
    }

根据元素来更新元素的方法,上文分析到,我们最终是要通过索引来操作,因此,我们可以在此封装一个给一个指定元素返回其索引的方法,代码段如下:


    /**
     * description 根据元素返回其索引,未找到返回-1
     *
     * @param element 需要查找的元素的索引
     * @return 返回索引
     */
    public int returnIndex(E element) {
        for (int i = 0; i < elements.length; i++) {
            if (elements[i].equals(element)) return i;
        }
        return ELEMENT_NOT_FOUND;
    }

然后就可以通过元素来更新元素啦(返回对应元素的索引)!代码块如下:


    /**
     * description 将一个元素替换为另一个元素,并返回被替换元素的索引
     *
     * @param e1 欲替换的元素
     * @param e2 替换后的元素
     * @return
     */
    public int update(E e1, E e2) {
        int index = returnIndex(e1);
        if (index < 0) return index;
        update(index, e2);
        return index;
    }

3.4元素的删除操作,两种方式,一种是根据索引来删除(要检查索引的合法性),一种是根据元素来删除。

根据索引删除(返回对应的元素)


    /**
     * description 根据索引删除元素
     * @param   index 需要删除的元素索引
     * @return 返回被删除的元素
     */
    public E remove(int index) {
        if (index < 0) return ELEMENT_NOT_DOES_EXIST;
        E element = elements[index];
        for (int i = index; i < size; i++) {
            elements[i] = elements[i + 1];
        }
        size--;
        return element;
    }

根据元素删除,在这删除之前调用returnElement(E element)方法

然后再调用根据索引删除的方法


    /**
     * description 按照元素删除指定的元素
     *
     * @param element 传入要删除的元素
     */
    public void remove(E element) {
        int index = returnIndex(element);
        remove(index);
    }

3.5根据索引查看元素


    /**
     * description 根据元素返回其索引,未找到返回-1
     *
     * @param element 需要查找的元素的索引
     * @return 返回索引
     */
    public int returnIndex(E element) {
        for (int i = 0; i < elements.length; i++) {
            if (elements[i].equals(element)) return i;
        }
        return ELEMENT_NOT_FOUND;
    }

3.6ArrayList判空,思路为:判定其size是否为0,代码如下


    /**
     * description 判断是否有元素
     * @return 布尔值
     */
    public boolean isEmpty(){
        return size==0;
    }

3.7清空元素,思路,仅需把有所有的元素置为null和size=0即可。代码块如下:

    /**
     * description 元素清空
     */
    public void clear(){
        for (int i = 0; i < size; i++) {
            elements[i]=null;
        }
        size=0;
    }

3.8 toString()方法

    @Override
    public String toString() {
        return "MyArrayList{" +
                "size=" + size +
                ", elements=" + Arrays.toString(elements) +
                '}';
    }

4、测试用例

/**
 * @author Mr.xiong
 * @version v1.0
 * @description ArrayList测试类
 * @time 2022/3/26 12:05
 * @since jdk8
 */
public class MyArrayListTest {
    public static void main(String[] args) throws IllegalAccessException {
        MyArrayList<Integer> myArrayList = new MyArrayList<>();
        for (int i = 0; i < 15; i++) {
            myArrayList.add(i+1);
        }
        myArrayList.add(0,14);
        System.out.println(myArrayList.update(2, (Integer)222));
        System.out.println(myArrayList.update((Integer)13,(Integer)444 ));
        System.out.println(myArrayList);
        System.out.println(myArrayList.returnIndex(15));
        myArrayList.remove((Integer)444);
        System.out.println(myArrayList.remove(2));
        System.out.println(myArrayList);
        myArrayList.clear();
        System.out.println(myArrayList.isEmpty());
        System.out.println(myArrayList);
    }
}

执行的结果如下:

 5、完整源代码

package mylist;

import java.util.Arrays;

/**
 * @author Mr.xiong
 * @version v1.0
 * @description 自定义实现arraylist
 * @time 2022/3/26 11:10
 * @since jdk8
 */
@SuppressWarnings("unchecked")
public class MyArrayList<E> {
    /**
     * description 元素的个数
     */
    private int size;

    /**
     * description 泛型类数组
     */
    private E[] elements;

    /**
     * description 定义初始化默认容量为10
     */
    private static final int DEFAULT_INITIAL_CAPACITY = 10;

    /**
     * description 元素未找到抛出-1
     */
    private static final int ELEMENT_NOT_FOUND = -1;

    /**
     * description 元素不存在
     */
    private final E ELEMENT_NOT_DOES_EXIST = null;

    /**
     * description 有参构造器,传入的参数小于小于默认值时采用默认值,否则采用传入的值
     *
     * @param capacity 传入的容量值
     */
    public MyArrayList(int capacity) {
        if (capacity >= DEFAULT_INITIAL_CAPACITY) {
            elements = (E[]) new Object[capacity];
        } else {
            elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY];
        }
    }

    /**
     * description 无参构造器,采用默认值
     */
    public MyArrayList() {
        this(DEFAULT_INITIAL_CAPACITY);
    }

    /**
     * description 添加元素的方法,默认从末尾追加
     *
     * @param element 传入一个添加的元素
     */
    public void add(E element) throws IllegalAccessException {
        add(size, element);
    }

    /**
     * description 从指定地方添加元素
     *
     * @param index   指定的索引
     * @param element 传入的元素
     * @throws IllegalAccessException 传入的索引越界异常
     */
    public void add(int index, E element) throws IllegalAccessException {
        isOutOfBoundsForAdd(index);
        ensureCapacity();
        for (int i = size; i > index; i--) {
            elements[i] = elements[i - 1];
        }
        elements[index] = element;
        size++;
    }

    /**
     * description 保证容量足够
     */
    private void ensureCapacity() {
        if (size < elements.length) return;
        /*
         * elements.length >> 1,位运算,相当于除以二,加快运算速度
         * 等价于 int C = elements.length *1.5;
         */
        int C = elements.length + (elements.length >> 1);
        E[] newElements = (E[]) new Object[C];
        //数组复制
        for (int i = 0; i < elements.length; i++) {
            newElements[i] = elements[i];
        }
        elements = newElements;
        System.out.println("已扩容,新容量为:" + elements.length);
    }

    /**
     * description 检查是否越界
     *
     * @param index 待检测的索引
     */
    private void isOutOfBoundsForAdd(int index) throws IllegalAccessException {
        if (index <= size && index >= 0) return;
        throw new IllegalAccessException("传入的索引越界");
    }

    /**
     * description 更新指定索引的元素
     *
     * @param index   需要更新元素的索引的值
     * @param element 传入的更新元素
     * @return 更新前的元素
     */
    public E update(int index, E element) {
        if(index>size||index<0)  return ELEMENT_NOT_DOES_EXIST;
        E e = elements[index];
        elements[index] = element;
        return e;
    }

    /**
     * description 将一个元素替换为另一个元素,并返回被替换元素的索引
     *
     * @param e1 欲替换的元素
     * @param e2 替换后的元素
     * @return
     */
    public int update(E e1, E e2) {
        int index = returnIndex(e1);
        if (index < 0) return index;
        update(index, e2);
        return index;
    }

    /**
     * description 根据元素返回其索引,未找到返回-1
     *
     * @param element 需要查找的元素的索引
     * @return 返回索引
     */
    public int returnIndex(E element) {
        for (int i = 0; i < elements.length; i++) {
            if (elements[i].equals(element)) return i;
        }
        return ELEMENT_NOT_FOUND;
    }

    /**
     * description 按照元素删除指定的元素
     *
     * @param element 传入要删除的元素
     */
    public void remove(E element) {
        int index = returnIndex(element);
        remove(index);
    }

    /**
     * description 根据索引删除元素
     * @param   index 需要删除的元素索引
     * @return 返回被删除的元素
     */
    public E remove(int index) {
        if (index < 0) return ELEMENT_NOT_DOES_EXIST;
        E element = elements[index];
        for (int i = index; i < size; i++) {
            elements[i] = elements[i + 1];
        }
        size--;
        return element;
    }

    /**
     * description 判断是否有元素
     * @return 布尔值
     */
    public boolean isEmpty(){
        return size==0;
    }

    /**
     * description 元素清空
     */
    public void clear(){
        for (int i = 0; i < size; i++) {
            elements[i]=null;
        }
        size=0;
    }

    @Override
    public String toString() {
        return "MyArrayList{" +
                "size=" + size +
                ", elements=" + Arrays.toString(elements) +
                '}';
    }
}

6、时间复杂度分析

由于数组在内存上地址是连续的

查询复杂度分析:查询的效率是O(1)级别,所以数组查询的效率特别高

添加的复杂度分析:最好O(1),最坏O(2n),平均O(n)

删除复杂度分析:最好O(1),最坏O(n),平均O(n)

更新复杂度:最好O(1)所以数组更新的效率特别高

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值