Java ArrayList的模拟实现

Posted 再见孙悟空

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java ArrayList的模拟实现相关的知识,希望对你有一定的参考价值。

package test;

import java.util.Arrays;
import java.util.Collection;

public class MyArrayList<E> {
    
    //存储数据
    private Object[] values;
    
    //已经使用的容量
    private int size;
    
    //修改次数
    private int modCount;
    
    /**
     * 根据给定容量初始化
     * @param initialCapacity
     */
    public MyArrayList(int initialCapacity){
        if(initialCapacity > 0){//按照给定的容量大小初始化数组
            values = new Object[initialCapacity];
        }else if(initialCapacity == 0){
            values = new Object[]{};
        }else{
            throw new IllegalArgumentException("非法初始化容量: "+
                    initialCapacity);
        }

    }
    
    /**
     * 默认初始化方法
     */
    public MyArrayList(){
        this(10);
    }
    
    /**
     * 用集合初始化一个ArrayList
     * @param c
     */
    public MyArrayList(Collection c){
        values = c.toArray();
        if (values.length != 0) {
            size = values.length;
            if (values.getClass() != Object[].class)
                values = Arrays.copyOf(values, size, Object[].class);
        }else{
            values = new Object[]{};
        }
    }
    /**
     * 检查是否数组越界了
     * @param index
     */
    private void rangeCheck(int index) {
        if (index >= size || index < 0)
            throw new IndexOutOfBoundsException("数组越界了");
    }
    
    /**
     * 替换原数组中的值,并且将原值返回
     * @param index
     * @param element
     * @return
     */
    public E set(int index,E element){
        //检查是否越界
        rangeCheck(index);
        
        E oldValue = (E)values[index];
        values[index] = element;
        
        return oldValue;
    }
    
    /**
     * 在数组的末尾增加一个元素
     * @param element
     */
    public void add(E element){
        //扩容
        ensureCapacity(size+1);
        values[size++] = element;
        
    }
    
    /**
     * @param index
     * @param element
     */
    public void add(int index,E element){
        //先检查数组的下标是否越界了
        rangeCheck(index);
        //扩容
        ensureCapacity(size+1);
        
        Object[] oldValues = values;
        values[index] = element;
        
        //将原数组index后的值拷贝带新数组中
        System.arraycopy(oldValues, index, values, index+1, size-index);
        
        size++;
        
    }
    
    /**
     * 将一个collection中的元素添加到数组末尾
     * @param c
     */
    public void addAll(Collection c){
        Object[] cArray = c.toArray();
        
        //扩容
        ensureCapacity(size+cArray.length);
        
        //将传入数据拷贝到数组末尾
        System.arraycopy(cArray, 0, values, size, cArray.length);
        
        size += cArray.length;
    }
    
    /**
     * 将一个collection中的元素添加到数组的指定位置
     * @param index
     * @param c
     */
    public void addAll(int index,Collection c){
        //先检查数组的下标是否越界了
        rangeCheck(index);
        //扩容
        Object[] cArray = c.toArray();
        int cLength = cArray.length;
        
        ensureCapacity(size+cLength);
        
        Object[] oldValues = values;
        //将传入数据拷贝到数组指定位置
        System.arraycopy(cArray, 0, values, index, cLength);
        
        //将原数组中index以后的数据拷贝过来
        System.arraycopy(oldValues, index, values, values.length-1, size-index);
        
        size += cLength;
        
    }
    
    /**
     * 根据下标获取元素
     * @param index
     * @return
     */
    public E get(int index){
        //先检查数组的下标是否越界了
        rangeCheck(index);
        
        E e = (E) values[index];
        
        return e;
        
    }
    
    /**
     * 根据下标删除元素
     * @param index
     * @return
     */
    public E remove(int index){
        //先检查数组的下标是否越界了
        rangeCheck(index);
        
        E removeValue = (E) values[index];
        
        System.arraycopy(values, index+1, values, index, size-index-1);
        
        //置为空,gc回收
        values[size--] = null;
        
        return removeValue;
        
    }
    
    /**
     * 根据对象删除
     * @param o
     */
    public void remove(Object o){
        //考虑元素为null的情况
        if(null == o){
            for (int i = 0; i < size; i++) {
                if(values[i] == null){
                    remove(i);
                }
            }
            
        }else{
            for (int i = 0; i < size; i++) {
                if(values[i].equals(o)){
                    remove(i);
                }
            }
            
        }
    }
    
    /**
     * 扩容方法,保证数组的长度足够
     * @param minCapacity
     */
    public void ensureCapacity(int minCapacity){
        modCount++;
        
        int oldCapacity = values.length;
        if (minCapacity > oldCapacity) {
            //先默认扩容50%+1,如果还是不能满足,则直接用传入的所需容量
            int newCapacity = (oldCapacity*3)/2+1;
            if(newCapacity < minCapacity){
                newCapacity = minCapacity;
            }
            
            values = Arrays.copyOf(values, newCapacity);
            
        }
    }
    
    public int size(){
        return size;
    }

    

}

最后再介绍一下ensureCapacity这个方法的想法(从网上看到的):

我们知道ArrayList的内部是采用数组来存储元素的,由于java数组都是定长的,所以这个数组的大小一定是固定的,这个大小就是capacity。我们可以肯定capacity一定是大于或等于ArrayList的size,那么当size不断增加到了要超过capacity的时候,ArrayList就不得不重新创建新的capacity来容纳更多的元素,这时需要首先建立一个更长的数组,将原来的数组中的元素复制到新数组中,再删除原来的数组。可见当ArrayList越来越大时,这种操作的消耗也是越来越大的。
为了减少这种不必要的重建capacity的操作,当我们能肯定ArrayList大致有多大(或者至少会有多大)时,我们可以先让ArrayList把capacity设为我们期望的大小,以避免多余的数组重建。
假设ArrayList自动把capacity设为10,每次重建时将长度递增原来的三分之二,那么当我们需要大约存储50个元素到ArrayList中时,就会大约需要重建数组4次,分别是在增加第11、第17、第26、第39个元素的时候进行的。如果我们一开始就让ArrayList的capacity为50,那么不需要任何数组重建就能完成所有插入操作了。
java允许我们在构造ArrayList的同时指定capacity,如new ArrayList(50),也允许在以后将它设得更大,而增大capacity就是使用ensureCapacity()方法。注意:capacity只能比原来的更大,而不能比原来的更小,否则java会忽略该操作。ArrayList的初始默认capacity为10,所以给capacity指定小于10的整数是毫无意义的。
最后说说ArrayList的size,前面说过,size一定小于等于capactiy,而且更重要的是,访问超过size的位置将抛出异常,尽管这个位置可能没有超过capacity。ensureCapacity()只可能增加capacity,而不会对size有任何影响。要增加size,只能用add()方法。





以上是关于Java ArrayList的模拟实现的主要内容,如果未能解决你的问题,请参考以下文章

[Java]ArrayList与LinkedList的模拟实现

Java模拟数据量过大时批量处理数据实现

C++之模拟ArrayList的操作(模板的使用)

求真正有效的可以模拟登录新浪微博的java代码,后续可以用Jsoup进行抓取。急急!!登录成功马上给分!

模拟ArrayList底层实现

例外:从意图存储ArrayList时“无法转换为java.util.ArrayList”