欢迎访问悦橙教程(wld5.com),关注java教程。悦橙教程  java问答|  每日更新
页面导航 : > > > 文章正文

android 获取ArrayList的Capacity,androidarraylist

来源: javaer 分享于  点击 5966 次 点评:63

android 获取ArrayList的Capacity,androidarraylist




今天,简单讲讲如何获取 

ArrayList的Capacity。


这里,需要了解一下ArrayList的源码。


一.ArrayList的源码解析


每个ArrayList实例都有一个容量,该容量是指用来存储列表元素的数组的大小。它总是至少等于列表的大小。随着向ArrayList中不断添加元素,其容量也自动增长。自动增长会带来数据向新数组的重新拷贝,因此,如果可预知数据量的多少,可在构造ArrayList时指定其容量。在添加大量元素前,应用程序也可以使用ensureCapacity操作来增加ArrayList实例的容量,这可以减少递增式再分配的数量。


这里所说的容量(Capacity)  不是我们通过ArrayList.getSize()获取的返回值,而是ArrayList里的数组的大小,即ArrayList开辟的内存的大小。


有文章说ArrayList默认构造的容量为10,没错。 因为ArrayList的底层是由一个Object[]数组构成的,而这个Object[]数组,默认的长度是10,所以有的文章会说ArrayList长度容量为10。


然而你所指的size()方法,指的是“逻辑”长度。
所谓“逻辑”长度,是指内存已存在的“实际元素的长度” 而“空元素不被计算”



即:当你利用add()方法,向ArrayList内添加一个“元素”时,逻辑长度就增加1位。 而剩下的9个空元素不被计算。

ArrayList<String> list = new ArrayList<String>();  
System.out.println("size = " + list.size());


输出结果如下:

size = 0


ArrayList默认size()是0.而这时的Capacity已经是10.


ArrayList源码解析

JDK版本不一样,ArrayList类的源码也不一样。

  • JDK1.8

ArrayList类结构

//通过ArrayList实现的接口可知,其支持随机访问,能被克隆,支持序列化
public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    //序列版本号
    private static final long serialVersionUID = 8683452581122892189L;

  //默认初始容量
    private static final int DEFAULT_CAPACITY = 10;

    //被用于空实例的共享空数组实例
    private static final Object[] EMPTY_ELEMENTDATA = {};

    //被用于默认大小的空实例的共享数组实例。其与EMPTY_ELEMENTDATA的区别是:当我们向数组中添加第一个元素时,知道数组该扩充多少。
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * Object[]类型的数组,保存了添加到ArrayList中的元素。ArrayList的容量是该Object[]类型数组的长度
     * 当第一个元素被添加时,任何空ArrayList中的elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA将会被
     * 扩充到DEFAULT_CAPACITY(默认容量)。
     */
    transient Object[] elementData; //非private是为了方便嵌套类的访问

    // ArrayList的大小(指其所含的元素个数)
    private int size;

    ......  

}


ArrayList包含了两个重要的对象:elementData 和 size。

构造函数

ArrayList提供了三种方式的构造器,可以构造一个默认初始容量为10的空列表、构造一个指定初始容量的空列表以及构造一个包含指定collection的元素的列表,这些元素按照该collection的迭代器返回的顺序排列的。

  /**
     * 构造一个指定初始容量的空列表
     * @param  initialCapacity  ArrayList的初始容量
     * @throws IllegalArgumentException 如果给定的初始容量为负值
     */
    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

    // 构造一个默认初始容量为10的空列表
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    /**
     * 构造一个包含指定collection的元素的列表,这些元素按照该collection的迭代器返回的顺序排列的
     * @param c 包含用于去构造ArrayList的元素的collection
     * @throws NullPointerException 如果指定的collection为空
     */
    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray()可能不会正确地返回一个 Object[]数组,那么使用Arrays.copyOf()方法
            if (elementData.getClass() != Object[].class)
                //Arrays.copyOf()返回一个 Object[].class类型的,大小为size,元素为elementData[0,...,size-1]
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }


ArrayList构造一个默认初始容量为10的空列表:

/**
     * public方法,让用户能手动设置ArrayList的容量
     * @param   minCapacity 期望的最小容量
     */
    public void ensureCapacity(int minCapacity) {
        int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
            // any size if not default element table
            ? 0
            // larger than default for default empty table. It's already
            // supposed to be at default size.
            : DEFAULT_CAPACITY;

        if (minCapacity > minExpand) {
            ensureExplicitCapacity(minCapacity);
        }
    }

    private void ensureCapacityInternal(int minCapacity) {
        //当elementData为空时,ArrayList的初始容量最小为DEFAULT_CAPACITY(10)
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        ensureExplicitCapacity(minCapacity);
    }

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

    //数组可被分配的最大容量;当需要的数组尺寸超过VM的限制时,可能导致OutOfMemoryError
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    /**
     * 增加数组的容量,确保它至少能容纳指定的最小容量的元素量
     * @param minCapacity 期望的最小容量
     */
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        //注意此处扩充capacity的方式是将其向右一位再加上原来的数,实际上是扩充了1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0) 
            newCapacity = hugeCapacity(minCapacity); //设置数组可被分配的最大容量
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }


附:jdk1.6中ensureCapacity(int minCapacity)方法:

// 确定ArrarList的容量。
// 若ArrayList的容量不足以容纳当前的全部元素,设置 新的容量=“(原始容量x3)/2 + 1”
    public void ensureCapacity(int minCapacity) {
        modCount++;  // 将“修改统计数”+1
        int oldCapacity = elementData.length;
        if (minCapacity > oldCapacity) {
            Object oldData[] = elementData;
            int newCapacity = (oldCapacity * 3)/2 + 1;
            if (newCapacity < minCapacity)
                newCapacity = minCapacity;
            elementData = Arrays.copyOf(elementData, newCapacity);
        }
    }


为什么ArrayList自动容量扩充选择扩充1.5倍?

这种算法构造出来的新的数组长度的增量都会比上一次大( 而且是越来越大) ,即认为客户需要增加的数据很多,而避免频繁newInstance 的情况。


这里,总结一下,ArrayList初始化的Capacity是10,当ArrayList的添加的元素大于Capacity时,ArrayList自动扩充1.5倍的Capacity。这个自动容量扩充会新建一个数组,然后把之前的数据考到新的数组里,然后添加新的数据,所以是比较耗时的操作。如果知道ArrayList的最大容量,最好在才初始化时进行指定,可以避免这个问题



二,获取ArrayList的Capacity


因为Capacity是私有的变量,一般是无法获取到的,不过可以通过反射获取。具体代码很简单:


import java.lang.reflect.*;
import java.util.*;

public class Test{
    public static void main(String[] args) throws Exception {
        ArrayList list = new ArrayList();
		Field f = ArrayList.class.getDeclaredField("elementData");
		f.setAccessible(true);
		Object[] elementData = (Object[])f.get(list);
		System.out.println(elementData.length);
    }
}

这样基本就可以获取到ArrayList的Capacity。


android 获取ArrayList的Capacity就讲完了。


就这么简单。

相关文章

    暂无相关文章

用户点评