zoukankan      html  css  js  c++  java
  • ArrayList 底层实现原理

    ArrayList的底层实现原理

    1, 属性:
    private static final int DEFAULT_CAPACITY = 10;
    
    private static final Object [] EMPTY_ELEMENTDATA = {};
    
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    
    transient Object [] elementData;
    
    private int size; // 动态数组的实际大小
    
     
    
    2,构造方法:
     public ArrayList(){
     
         this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
     
     }
     调用无参的构造方法,则会将elementData赋上为空的一个数组.
    
      
    public ArrayList(Collection<? extends E> c){
        elementData  = c.toArray();
        if(size = elementData.length != 0){
            if(elementData.getClass()!=Object[].class)
                elementData = Arrays.copyOf(elementData,size,Object[].class);
        }else{
            elementData = EMPTY_ELEMENTDATA;
        }
    }
    
    调用有参的构造方法,参数是一个带范型的集合。生成一个可以不为空的集合。
    如果参数使用toArray(),返回类型不是Object[],则使用copyOf() 复制下并赋值给elementData,
    
     
    
    public ArrayList(int initialCapacity){
         if(initialCapacity > 0){
             this.elementData = new Object[initialCapacity];
         }else if(initialCapacicty == 0){
             this.elementData = EMPTY_ELEMENTDATA;
         }else{
             throw new IlleagalArgumentException("illagal initialCapacity"+initialCapcity);
         }
    }
    
    调用有参的构造函数,初始化储存元素的容量。定义之后,内存会为开辟初始化容量大小的空间。
    随着ArrayList 里面元素的增加直到initialCapacity,不需要数组向新数组的拷贝。
    
    Tips:
    Arraylsit list = new ArrayList(10);
    默认的Object[] elementData 的长度为10;
    list.size();的长度还是为0
    
    因为size 代表的逻辑长度,内存中实际存在的元素的长度。
    
     
    
    3,add;
    
    public boolean add(E e){
       ensureCapacityInternal(size + 1);
       elementData[size++] = e;
       return true;
    }
    
    private void ensureCapacityInternal(int minCapacity){
       if(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA){
         minCapacity = Math.max(DEFAILT_CAPACITY,minCapacity);
       }
       ensureExplicitCapacity(minCapacity);
    }
    
    向ArrayList 里面增添元素的时候,当ArrayList 的为空的时候,add元素时,如果没有设定Arraylist的Object elementData的长度,
    会设定默认的Object[] elementData 的长度为10,所以在长度为10 之前能够避免在每添加一个新的元素时候,就要复制原来的元素的数组到新的数组的里面去。
    
    
    private void ensureExplicitCapacity(int minCapacity){
        modCount ++;//extends from AbstactArrayList
        
        if(minCapacity - elementData.length > 0)
            grow(minCapacity);//长度不够,每次扩充1.5倍<粗略>
        
    }
    
    private void grow(int minCapacity){
       int oldCapacity = elementData.length;
       int newCapacity = oldCapacity + (oldCapacity >> 1);
       if(newCapacity - minCapacity <0)
           newCapacity = minCapacity;
       if(newCapacity - MAX_ARRAY_SIZE > 0)
           newCapacity = hugeCapacity(minCapacity);
       //MAX_ARRAY_SIZE 2147483639
    
       elementData = Arrays.copyOf(elementData,newCapacity);//扩充了Obect elementData[]的长度
    }
    
    
    private void hugeCapacity(int minCapacity){
        if(minCapacity < 0)//因为minCapacity int 型,超过int 的最大值就会<0,就代表内存溢出
            throw new OutOfMemoryError();
        return(minCapacity > Max_ARRAY_SIZE)?Integer.MAX_VALUE:MAX_ARRAY_SIZE;
    //最大上限就是Integer的最大值-8;
    }
    
     
    
    4,add
    public void add(int index,E element){
        rangeCheckForAdd(index);
        ensureCapacityInternal(size + 1); //开辟空间
        System.arraycopy(elemntData,index,elementData,index + 1,size - index);
        size ++;
    }
    
    
    private void rangeCheckForAdd(int index){
        if(index > size || index < 0 ){
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
        }
    }
    
    add(int index,E elemenmt)中使用System.arraycopy的思想,是因为elementData的容器长度大于逻辑长度,即它本身的留有一个空位null,可用作填充数据。
    
    例:
    Object [] elementData = {"one","two","three","four","five","null","null","null"};
    要想在index为3的位置插入"six",
    System.arraycopy(elementData,3,elementData,4,2);
    elementData:源数组
    3:源数组的起始位置
    elementData:目标数组
    4:目标数组的开始位置
    2:size - index,size代表的是elementData中不为null的元素的长度
    
    所以,就可以将index + 1 后半段的不为null 的元素复制到源数组的本身,同时将index 这个位置空出来,然后插入就可以实现add插入指定位置的功能。
    elementData[index] = "six";
    size ++;
    
     
    
    Arrays.copyOf(),System.copyof()
    system.arraycopy(Object src,int srcPos,Object dest,destPos,length);native 方法,源码是由C++ 实现
    Arrays.copyOf(),内部实现的也是System.arraycopy(),只是ArrayCopy()会创建一个新的数组,将源数组向新数组复制。
    
     
    
    5,remove
      public E remove(int index){
        rangeCheck(index);//先check下index有没有有越界
        modCount++;//非安全线程
    
        E oldValue = elementData[index];
    
        int numMoved = size - index - 1;
    
        if(numMoved > 0)
        System.arraycopy(elementData,index + 1,elementData,numMoved);
    
        //如果numMoved == 0 的情况也就是源数组只有一个元素的情况,就无需复制数组,只需执行elemnetData[--size]即elementData[0] =null
        elementData[--size] = null;//将elementData 的空间变成null
    
        return oldValue;
    }
    
    private void rangeCheck(int index){
        if(index >= size){
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
        }
    }
    
    从一定程度上来说,remove(int index)的原理是和add(int index, E element)的逻辑是差不多的。
    
    
    6,remove
    public boolean remove(Object o){
        if(o == null){
            for(int index = 0;index < size;i++){
                if(elementData[index]==null){
                    fastRemove(index);
                    return true;
                }
            }
    
        }else{
                for(int index = 0;index < size;i++){
                if(o.equals(elementData[index])){
                    fastRemove(index);
                    return true;
                }
            }
     
        }
        return false;
    }
    
    private void fastRemove(index){
         modCount++;//非安全线程
    
        int numMoved = size - index - 1;
    
        if(numMoved > 0)
        System.arraycopy(elementData,index + 1,elementData,numMoved);
    
        elementData[--size] = null;//将elementData 的空间变成null
    
    }
    
    remove(Object o) 删除集合中匹配的元素<最先匹配的元素>,其思路和remove(index)是一样的
    remove(index)是直接告知index,remove(Obejct o)是先找到匹配的index,然后再进行和remove(index) 大致的操作。
    
    其中,remove(Obejct o)需考虑元素o 是否为null,关于两元素是否相等的比较方法== 和 equals
    == 在值类型中,就是比较值是否相等,但是在引用类型中,实际比较的是在内存中的地址是否相等
    equals,是Obejct 中的方法,如下:
     
    public boolean equals(Object o){
        return (this == o);
    }
    
    Object 是所有类的父类,Obejct 中的equals 方法就是==,是比较内存中的地址是否相等,
    许多类会重写Obejct 中的equals方法,例如String 类,重写之后就是对字符串值进行比较
    
    
    在remove(Obejct o) 中的equals 方法实际比较的就是内存中地址的相等。但是如果对于对Object中equals 方法重写的类,
    则实际比较的就是值是否相等。
    
    



  • 相关阅读:
    Android 查看通讯录Contacts是否发生变化
    卓尼斯ZT-180评測
    C++中的单例模式
    Android 动画之ScaleAnimation应用具体解释
    java的静态代理
    词性标注
    ubuntu 11.04安装笔记
    机房收费系统学生下机结账小结
    MyBatis入门学习(一)
    !!!!OpenWrt系列教程汇总
  • 原文地址:https://www.cnblogs.com/pickKnow/p/9151463.html
Copyright © 2011-2022 走看看