动态数组的实现 - java

1、说明

动态数组在 JDK中有其源代码,类的名字叫做 ArrayList,本文是对于其部分方法的复现,部分方法的实现略有不同,通过下面代码的学习,可以对于 JDK 中的动态数组有更佳直观的认识,对于源代码的阅读也有一定的好处

2、作者自己实现的动态数组方法如下所示:

在这里插入图片描述

3、作者自己实现的原始代码

/**
 * 对于范型的操作
 * 1、在写类的时候,表明这是一个范型()范型类
 */

/**
 * 对象数组
 * Object[] object = new Object[7];
 * object[0] = new Person(10,"Hello");
 * 对象数组中保存的元素是对象的引用地址,不是对象本身;
 * 上面的 object[0] 存储的是对象的地址,不是对象本身;
 * <p>
 * 为什么放地址?
 * 1、节省空间
 * 2、假设对象占有多个字节,很难在索引地方塞下
 */

public class myArrayList<E> {
    public static final int ELEMENT_NOT_FOUND = -1;
    // static 静态的 在代码区中只有一份 变量
    // 定义无参构造的对象的默认大小
    private static final int DEFAULT_CAPACITY = 10;
    /*
        定义了两个成员变量
            数组的大小
            数组的存储元素 E<> 使用了泛型,进行元素的存储
     */
    private E[] elements; // 开辟的内存空间是 E 数据类型的连续存储
    private int size; // 下一个要放置的位置索引  [0,size-1]是已经放置好的元素

    //有参构造
    public myArrayList(int capacity) {
        // 使用三目运算符,判断,使用的容量的大小
        capacity = (capacity <= DEFAULT_CAPACITY) ? DEFAULT_CAPACITY : capacity;

        // 进行强制的类型转换
        // new 创建的数组大小,使用 Object 可以存放多种数据类型
        // 创建数组的语法:new int[10];
        // 在最后必须进行强制的类型转换即可
        elements = (E[]) new Object[capacity];//新建容量大小
        size = 0;//数组里元素个数
    }

    //无参构造 默认的空间大小为 10
    public myArrayList() {
        // 通过无参数的构造函数,调用有参数的构造函数,使用 this
        this(DEFAULT_CAPACITY);
    }

    //判断是否为空
    public boolean isEmpty() {
        return size == 0;
    }

    //返回元素个数
    public int getSize() {
        return size;
    }

    // 向数组中添加元素
    // 添加到数组的最后面
    public void add(E e) {
        // 此处调用了加入的方法,根据位置,进行元素的加入
        add(size, e);
    }

    //向头部添加元素
    public void addFirst(E e) {
        add(0, e);
    }

    // 在数组中的任意位置,进行元素的添加
    // 从数组的最后面移动,空出来 index
    // 删除是小的先向前面移动
    public void add(int index, E e) {
        // 对于空数据的处理
//        if (e == null) return;

        // 判断当前的索引是否是正确的,正确之后,进行元素的添加
        // 此处的插入中, index > size 可以等于 size 因为相当于在数组的最后面插入,是可以插入进去的,前提是小于容器的最大索引
        rangeCheckForAdd(index);

//        if (index < 0 || index > size) {
//            throw new IndexOutOfBoundsException("超出了索引");
//        }

        // 进行扩容操作
        // size 的容量就是当前的数组可以存储的最多元素的数量,比它大的时候,当这个数量满的时候,进行扩容即可
        // size 真正存在的元素的个数
        if (size == elements.length) {
            resize(2 * elements.length);
        }

        // 添加的位置,给空出来,然后加进去
        // 循环的条件是:从 index 一直到  size - 1 移动即可
        for (int i = size - 1; i >= index; i--) {
            elements[i + 1] = elements[i];
        }
        elements[index] = e;
        size++;
    }

    //扩容操作
    private void resize(int i) {
        // 申请更大的数组,将数组拷贝过去
        // new int[] new Object[] 之间是有区别的,创建对象数组,创建普通数组
        E[] newarr = (E[]) new Object[i];
        for (int j = 0; j < size; j++) {
            newarr[j] = elements[j];
        }

        // 引用地址改变给原来的名字
        // newarr 垃圾回收器会自动回收
        elements = newarr;
    }

    //打印数组
    @Override
    public String toString() {
        // java 中进行字符串的拼接 使用 StringBuilder,提升效率
        StringBuilder res = new StringBuilder();
        res.append("size=" + size + "\nacpiticy=" + elements.length + "\n\n");
        res.append("[");
        for (int i = 0; i < size; i++) {
            res.append(elements[i]);

            // size 是数组的大小,实际比索引是大一个的,所以此处需要减去 1
            if (i != size - 1) {
                res.append(",");
            }
        }
        res.append("]");
        return res.toString();
    }

    //取出元素
    public E get(int index) {
        // index 进行约束
        rangeCheck(index);
//        if (index < 0 || index >= size) {
//            // 拼接字符串,抛出异常
//            throw new IndexOutOfBoundsException("Index:" + index + ",Size:" + size);
//        }
        return elements[index];
    }

    // 给指定位置赋值
    public void set(int index, E e) {
        if (index < 0 || index >= size) {
            throw new IndexOutOfBoundsException("Index:" + index + ",Size" + size);
        }

        // 找到索引,进行赋值
        elements[index] = e;
    }

    // 数组中是否包含制定元素
    public boolean contains(E e) {
        for (int i = 0; i < size; i++) {
            if (elements[i].equals(e)) {
                return true;
            }
        }
        return false;
    }

    // 获取数组中指定元素的索引
    // 在泛型中,传进来的对象数据类型,与实际的泛型中保存的泛型进行对比
    public int find(E e) {
        for (int i = 0; i < size; i++) {
            if (elements[i].equals(e)) {
                return i;
            }
        }
        return ELEMENT_NOT_FOUND;
    }

    // 删除第一个数
    public E removeFirst() {
        return remove(0);
    }

    // 删除最后一个数
    public E removeLast() {
        return remove(size - 1);
    }

    // 删除指定索引数
    public E remove(int index) {
        // 对于索引的检查
        rangeCheck(index);

        E res = elements[index];
        // 索引后面的元素,集体移动
        for (int i = index + 1; i < size; i++) {
            // 进行向前移动
            elements[i - 1] = elements[i];
        }

        // 当最后一个元素,移动到前面的时候,最后一个元素不用处理,因为 size 已经进行了移动,后面的元素是无法进行访问的
        size--;

        // 对象数组的最后面,进行对象的销毁
        elements[size] = null;

        // 对于代码进行扩容的操作
        if (size == elements.length / 4) {
            resize(elements.length / 2);
        }
        return res;
    }

    // 删除指定元素
    public void removeElement(E e) {
        int index = find(e);
        if (index != -1) {
            remove(index);
        }
    }

    // 查找元素,找到之后,返回其索引
    public int indexOf(E element) {
        // 允许了空值的传递,此处调用 下面的 equals() 会报错,需要处理
        if (element == null) {
            // 找到的数组中第一个空元素
            for (int i = 0; i < size; i++) {
                if (elements[i] == null) return i;
            }
        } else {
            for (int i = 0; i < size; i++) {
                // 判断当前的对象与传入进去的对象是否一致
                // == 只是会比较引用地址
                // 把 element 放到前面,因为已经判断了,过来一定是非空的,使用 equals() 比较即可
                if ((element.equals(elements[i]))) {
                    return i;
                }
            }
        }
        return ELEMENT_NOT_FOUND;
    }


    /**
     * clear
     * 直接使得 size = 0 ,表面上实现了数组的清空,可以节约a运行时间
     * <p>
     * 清理对象数组的顺序,引用数据,指向堆中的对象,之间的连接线断开之后,对象将会被垃圾回收
     */
    public void clear() {
        // 对于对象数组,单单使用 size = 0,会导致对象无法被完全的回收,造成内存的浪费,需要使得所有的对象的引用地址都为 null
        // 将内存地址清空

        // element == null 直接将数组干掉,没有必要,需要进行重新创建,消耗时间
        // 让数组里面指向的对象干掉
        // 可以循环利用的留下来,减少时间的消耗
        for (int i = 0; i < size; i++) {
            elements[i] = null;
        }

        size = 0;
    }

    // 验证添加元素的索引
    private void rangeCheckForAdd(int index) {
        if (index < 0 || index > size) {
            throw new IndexOutOfBoundsException("Index:" + index + ",Size" + size);
        }
    }

    // 验证其他的非添加时候的索引,索引不能等于最大个数,因为会超过容器大小
    // private 只有在同一个类中是可以调用的
    // public 任何类中都可以调用
    private void rangeCheck(int index) {
        if (index < 0 || index >= size) {
            throw new IndexOutOfBoundsException("Index:" + index + ",Size" + size);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值