ArrayList集合

简介: ArrayList集合

公众号merlinsea


  • ArrayList
  • ArrayList如果采用默认不带参数的构造函数,那么数组初始长度为0。线程不安全,只有第一次加入了元素以后才会把容量默认增加到10。
  • 扩容的机制是:newlen = oldlen + oldlen/2
  • ArrayList扩容机制代码解释:
  • 第一步:按照正常情况进行扩容:newCapacity = oldCapacity + oldCapacity/2
  • 第二步:如果正常扩容后的大小newCapacity比需要的最小容量minCapacity小,那么把newCapacity=minCapacity
  • 第三步:判别newCapacity是否大于允许的最大值,如果是的话就设置newCapacity为最大值。


640.jpg


  • grow(minCapacity) 表示我需要把数组扩容到最小为minCapacity的大小
  • 首先按照正常扩容到方法 newLen = oldLen + oldLen/2
  • 如果newLen >minCapacity  那么就扩容到newLen
  • 如果newLen <= minCapacity ,那么就看看minCapacity会不会撑爆内存,如果不会就扩容到minCapacity,否则就扩容到最大值
  • ArrayList的使用建议
  • 当已经知道需要操作多少数量的元素时候,最好是指定容量,防止arraylist在后续添加的过程中自动扩容产生性能开销。

  • ArrayList手写实现的核心点
  • 有一个size用于记录实际上存储元素的长度大小
  • 每次add()一个元素时,要保证size+1有空的位置来存放元素
  • 在保证size+1的位置时,ensureCapacity()函数,如果最小位置比数组长度大,说明需要扩容,扩容的时候首先按照newLen = oldLen + oldLen/2进行扩容,如果扩容后的newLen依旧不满足minCapacity的要求,就把newLen=minCapacity。然后判别newLen是否超出了最大长度。


public class MyArrayList implements Serializable {
    //使用这个字段,来判断当前集合类是否被并发修改,即迭代器并发修改的fail-fast机制
    private transient int modCount = 0;
    //第一次扩容的容量
    private static final int DEFAULT_CAPACITY = 10;
    //用于初始化空的list
    private static final Object[] EMPTY_ELEMENT_DATA = {};
    //实际存储的元素,transient不能被序列化
    transient Object[] elementData;
    //实际list集合大小,从0开始
    private int size=0;
    public MyArrayList(){
        this.elementData = EMPTY_ELEMENT_DATA;
    }
    public MyArrayList(int initialCapcity){
        if(initialCapcity>0){
            this.elementData = new Object[initialCapcity];
        }else if(initialCapcity == 0){
            this.elementData = EMPTY_ELEMENT_DATA;
        }else {
            throw new IllegalArgumentException("参数异常");
        }
    }
    public boolean add(Object e){
        //判断容量
        ensureCapacityInternal(size+1);
        //使用下标赋值,尾部插入
        elementData[size++] = e;
        return true;
    }
    //计算容量+确保容量
    private void ensureCapacityInternal(int minCapacity){
        //用于并发判断
        modCount++;
        //如果是初次扩容,则使用默认的容量
        if(elementData == EMPTY_ELEMENT_DATA){
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        //是否需要扩容,需要的最少容量大于现在数组的长度则要扩容
        if(minCapacity - elementData.length > 0){
            int oldCapacity = elementData.length;
            int newCapacity = oldCapacity + (oldCapacity>>1);
            //如果新容量 < 最小容量, 则讲最新的容量赋值给新的容量
            if(newCapacity - minCapacity < 0){
                newCapacity = minCapacity;
            }
            //创建新数组
            Object[] objects = new Object[newCapacity];
            //将旧的数组复制到新的数组里面
            System.arraycopy(elementData,0, objects,0,elementData.length);
            //修改引用
            elementData = objects;
        }
    }
    /**
     * 通过下标获取对象
     * @param index
     * @return
     */
    public Object get(int index){
        rangeCheck(index);
        return elementData[index];
    }
    private void rangeCheck(int index){
        if(index > size || size < 0){
            throw  new IndexOutOfBoundsException("数组越界");
        }
    }
    /**
     * 判断对象所在的位置
     * @param o
     * @return
     */
    public int indexOf(Object o){
        if(o == null){
            for(int i=0; i < size; i++){
                if(elementData[i] == null){
                    return i;
                }
            }
        }else {
            for(int i=0; i<size; i++){
                if(o.equals(elementData[i])){
                    return i;
                }
            }
        }
        return -1;
    }
    public Object set(int index, Object obj){
        rangeCheck(index);
        Object oldValue = elementData[index];
        elementData[index] = obj;
        return oldValue;
    }
    /**
     * 根据索引删除元素
     * @param index
     * @return
     */
    public Object remove(int index){
        rangeCheck(index);
        //用于并发判断
        modCount++;
        Object oldValue = elementData[index];
        //计算要删除的位置后面有几个元素
        int numMoved = size - index -1;
        if(numMoved>0){
            System.arraycopy(elementData,index+1,elementData,index,numMoved);
        }
        //将多出的位置为空,没有引用对象,垃圾收集器可以回收,如果不为空,将会保存一个引用,可能会造成内存泄露
        elementData[--size] = null;
        return oldValue;
    }
    //获取数组实际大小
    public int size(){
        return this.size;
    }
}


算法训练营永久刷题班元旦优惠价,超低价格永久刷题。加入我们可以一起备战明年的春招笔试,快来参加吧~

算法永久刷题班元旦优惠价~~

奔跑的小梁,公众号:梁霖编程工具库
leetcode刷题直播教学,手把手带你刷题,元旦价格优惠通知,超低价格永久刷题


相关文章
|
1月前
|
存储 安全 Java
32.C#:ArrayList集合
32.C#:ArrayList集合
16 1
|
4月前
|
C#
C# 集合(ArrayList)的方法和使用
C# 集合(ArrayList)的方法和使用
|
4月前
|
存储 Java API
Java集合之List集合(下)
Java集合之List集合(上)
44 0
|
4月前
|
存储 Java 索引
Java集合之List集合(上)
Java集合之List集合
69 0
|
5月前
|
存储 uml 容器
集合框架之List集合
集合框架之List集合
40 0
|
6月前
|
存储 容器
集合框架系列(一)之 list集合
集合框架系列(一)之 list集合
|
6月前
集合框架-list集合
集合框架-list集合
21 0
|
8月前
TreeSet集合
TreeSet集合
29 0
|
9月前
|
存储 安全 C#
C#里面的不同集合(数组、ArrayList集合、List泛型)
在内存中连续存储,因此可以快速而容易地从头到尾遍历元素,可以快速地修改元素
|
10月前
|
存储 Java
使用Set集合及HashSet,TreeSet
使用Set集合及HashSet,TreeSet
42 0