java实现ArrayList

本文深入解析ArrayList的内部实现,包括数组扩容、元素插入与删除等关键操作,通过自定义实现帮助理解其工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在java中,集合的操作 可以说是在平常不过了。对于集合可能大部分情况下都只是掌握它们的使用,其实对于它们的内部实现还是有必要了解的。这样对于学习java是一种提升。那么下面我们来学习一下ArrayList,Stack,linkedlist,hashMap四种集合框架的内部实现。
首先我们从最简单的开始ArrayList,顾名思义是数组集合,它的内部实现是基于数组的,也就是说内存空间地址是连续的,这中线性结构对于我们随机访问是非常高效的。观看源码可以发现,这个类的设计先继承一个抽象类,然后抽象类继承List接口,这里抽象类的对于就是可以先实现一些简单的方法,不想实现的可以继续交给子类来实现。但是本节重点在于怎么实现,所以我们将抽象类这个环节抛弃,直接定义接口,实现接口重而实现ArrayList。代码如下:详细注释。

package com.yxc.util;

/**
 * 自己实现ArrayList集合
 * 定义一个顶层接口,定义各种方法
 * 我们这里使用泛型编程,方便后面的书写
 * 这里我们继承Iterable接口,这样我们就可以实现遍历
 */
public interface MyList<E> extends Iterable<E>{
    /**
     * 在集合末尾添加一个元素
     * @param elem
     */
    public void add(E elem);

    /**
     * 在结合特定的位置添加一个元素
     * @param index
     * @param elem
     */
    public void add(int index,E elem);

    /**
     * 获取指定位置的元素
     * @param index
     * @return E
     */
    public E get(int index);

    /**
     * 设置指定位置的值,然后返回原来的元素
     * @param index
     * @param e
     * @return
     */
    public E set(int index,E e);

    /**
     * 从前往后遍历查找元素,返回下标
     * @param elem
     * @return index
     */
    public int indexOf(E elem);

    /**
     * 从后往前索引元素,返回下标
     * @param elem
     * @return index
     */
    public int lastIndexOf(E elem);

    /**
     * 判断集合中是不是包含某一对象
     * @return
     */
    public boolean Contain(E e);

    /**
     * 删除指定小标的元素 返回被删除元素
     * @param index
     * @return
     */
    public E remove(int index);

    /**
     * 删除指定元素,返回是不是成功
     * @param elem
     * @return
     */
    public boolean remove(E elem);

    /**
     * 集合的长度
     * @return
     */
    public int size();

    /**
     * 清空集合
     */
    public void clear();

    /**
     * 判断集合是不是为空
     * @return
     */
    public boolean isEmpty();

    /**
     * 将容量和真实大小统一,只用在数组上面。
     */
    public void trimToSize();

}

实现类:

package com.yxc.util;

import java.util.Iterator;

/**
 * 具体的实现类,在java源码中,其实还有一个抽象类的,抽象类的作用就是先完成一些简单的实现,
 * 不想实现的方法可以先不实现,这里我们为了学习简单点,直接实现所以的方法
 */
public class MyArrayList<E> implements MyList<E>{
    //首先线性表是基于数据实现的,所以要定义数据的长度和初始化数组
    public static final int INIT_CAPACITY=15;  //默认数组的空间大小
    //初始化集合的大小为0
    public int size=0;
    //因为我们在类上面都是用了泛型,所以这里定义数据我们可以直接使用泛型
    public E elements[]=(E[])new Object[INIT_CAPACITY];

    /**
     * 对于数组的操作,肯定就会有容量的问题,所以肯定要有一个方法来实现扩容的操作
     * 传入的参数是新的容量
     */
    public void ensureCapacity(int newCapacity){
        //如果我们扩容的数量小于数据的长度,那么显然扩容失败
        if(elements.length>=newCapacity){
            return;
        }
        //如果正常情况,那么就需要扩容
        //扩容就是创建一个新的数组,容量比之前的大,而且要将原来数组的元素复制进去
        //首先要将原来的数组保存一下
        E[] oldElement=elements;
        //创建一个新的数组,容量就是扩容以后的长度
        E[] newElements=(E[])new Object[newCapacity];
        //接着就是就是将原数组中的元素复制过来 我们可以使用for循环,但是这边我们有一个方法
        System.arraycopy(oldElement,0,newElements,0,size);
        //将新的数据赋值引用给elements
        elements=newElements;
        //释放对象,这个引用只是为了暂时保存变量,使用以后就可以释放
        oldElement=null;
    }

    //对于新增,插入都会有一个index检查,如果越界那就是非法操作
    public void checkIndex(int index){
        if(index<0||index>size){
            throw new IndexOutOfBoundsException("下标越界 请检查下标的合法"+index);
        }
    }

    @Override
    public void add(E elem) {
        //有了下面的方法以后,那么这个方法就很好获取了
        //直接在尾部插入元素
        add(size,elem);
    }

    @Override
    public void add(int index, E elem) {
        //首先检查下标的合法性
        checkIndex(index);

        //如果容量不够,扩容 默认大小两倍
        if(size==elements.length){
            ensureCapacity(INIT_CAPACITY*2+1);
        }
        //添加一个元素,首先就是将要添加位置的元素往后移动一位,然后将要添加的元素直接复制给index位置
        //可以使用循环赋值,这里还是使用数组的复制方法
        System.arraycopy(elements,index,elements,index+1,size-index);
        //然后将值赋值给指定位置
        elements[index]=elem;
        //最后长度要++
        size++;
    }

    @Override
    public E get(int index) {
        checkIndex(index);
        return elements[index];
    }

    @Override
    public E set(int index, E e) {
        checkIndex(index);
        //用一个变量保存好index位置的数据
        E temp=elements[index];
        //将新元素添加进去
        elements[index]=e;
        //最后返回原来的数据
        return temp;
    }

    @Override
    public int indexOf(E elem) {
        //循环查找元素
        for(int i=0;i<elements.length;i++){
            //如果找到直接返回序下标,不然返回-1
            if(elem.equals(elements[i])){
                return i;
            }
        }
        return -1;
    }

    @Override
    public int lastIndexOf(E elem) {
        //从集合的最后开始遍历
        for(int i=elements.length-1;i>=0;i--){
            if(elem.equals(elements[i])){
                return i;
            }
        }
        return -1;
    }

    @Override
    public boolean Contain(E e) {
        int i = indexOf(e);
        return i!=-1;
    }

    @Override
    public E remove(int index) {
        //删除一个元素,返回删除的元素
        checkIndex(index);
        //将要删除的元素保存起来
        E temp=elements[index];
        //然后移动数组元素,我们还是使用arraycopy函数,可以使用for循环
        System.arraycopy(elements,index+1,elements,index,size-index-1);
        //最后记得size--
        size--;
        //返回我们删除了的元素
        return temp;
    }

    @Override
    public boolean remove(E elem) {
        //先将元素查找到,如果不存在,那么直接删除失败,如果存在,调用它的重载方法直接删除就可以
        int index = indexOf(elem);
        if(index!=-1){
            remove(index);
            return true;
        }
        return false;
    }

    @Override
    public int size() {
        return size;
    }

    @Override
    public void clear() {
        //清空集合操作就是将集合长度设置为0,然后对集合的容量重新设置
      size=0;
      elements=(E[])new Object[INIT_CAPACITY];
    }

    @Override
    public boolean isEmpty() {
        //判断集合长度是不是为空就可以
        return size==0;
    }

    @Override
    public void trimToSize() {
        //这个方法是让集合的容量和真实大小统一
        ensureCapacity(size);
    }

    //MyList中实现了迭代器的接口,所以这里会实现这个方法
    //对于这个方法的实现我们需要创建一个内部类
    //使用内部类的好处就是外部类中的变量可以直接使用
    @Override
    public Iterator<E> iterator() {
        //需要返回一个Iterator,所以我们这里写一个内部类
        return new MyIterator();
    }
    //内部类实现Iterator接口,实现里面的两个方法
    class MyIterator implements Iterator<E>{
        //当前迭代器指向0
        private int curIndex=0;
        @Override
        public boolean hasNext() {
            //是不是还有下一个元素
            //只要当前下标的值小于集合的长度,那么说明还有下一个元素
            return curIndex<size;
        }
        //返回下一个元素
        @Override
        public E next() {
            //如果有下一个元素,那么将值返回 否则抛出异常。
            if(!hasNext()){
                throw new IndexOutOfBoundsException("越界了");
            }
            //返回以后下标要自增
            return elements[curIndex++];
        }
    }
}


测试类:

package com.yxc.util;

import java.util.Iterator;

/**
 * 测试自己手写的ArrayList是不是有效
 */
public class ArrayListTest {
    public static void main(String[] args) {
        MyList<Integer> myList=new MyArrayList<Integer>();
        for(int i=0;i<20;i++){
            myList.add(i);
        }
        for (int i=0;i<myList.size();i++){
            System.out.println(myList.get(i));
        }
        //检查checkIndex的用处
        // System.out.println(myList.get(10));
        //下面测试一些方法
//        System.out.println(myList.size());
//        System.out.println(myList.isEmpty());
//        System.out.println(myList.indexOf(3));
//        System.out.println(myList.lastIndexOf(1));
//        System.out.println(myList.Contain(3));

//        Integer elem = myList.remove(3);
//        System.out.println(elem);
//        System.out.println(myList.size());
//        myList.remove(1);
//        System.out.println(myList.size());

        //下面我们测试一下自己写的迭代器
        Iterator<Integer> iterator = myList.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }


    }

}

其实如果学过数据结构,那么这一个实现是很简单的,比较困难的地方可能就是扩容以及有一个函数的使用System.arraycopy()因为参数有点多
这是一个复制数组的函数 。我们这里举个例子:假设有数组
int[] a=new int[]{1,4,5,8,2,6,7};
int[] b=new int[7];
我们现在想要把数组a中的元素复制到数组 b中,那么函数就可以这样使用
System.arraycopy(a,0,b,0,a,length)
数组a中的元素从第零号开始复制到数组b中0号开始,复制的长度是a数组的长度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值