今天,简单讲讲如何获取



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。

  1. elementData 是”Object[] 类型的数组”,它保存了添加到ArrayList中的元素。实际上,elementData是个动态数组,我们能通过构造函数 ArrayList(int initialCapacity)来执行它的初始容量为initialCapacity;如果通过不含参数的构造函数ArrayList()来创建 ArrayList,则elementData的容量默认是10。elementData数组的大小会根据ArrayList容量的增长而动态的增长,具 体的增长方式,请参考源码分析中的ensureCapacity()函数。

  2. 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的空列表:

  1. 初始情况:elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; size = 0;

  2. 当向数组中添加第一个元素时,通过add(E e)方法中调用的ensureCapacityInternal(size + 1)方法,即ensureCapacityInternal(1);

  3. 在ensureCapacityInternal(int minCapacity)方法中,可得的minCapacity=DEFAULT_CAPACITY=10,然后再调用ensureExplicitCapacity(minCapacity)方法,即ensureExplicitCapacity(10);

  4. 在ensureExplicitCapacity(minCapacity)方法中调用grow(minCapacity)方法,即grow(10),此处为真正具体的数组扩容的算法,在此方法中,通过elementData = Arrays.copyOf(elementData, 10)具体实现了elementData数组初始容量为10的构造。

    调整数组的容量

      从add()与addAll()方法中可以看出,每当向数组中添加元素时,都要去检查添加元素后的个数是否会超出当前数组的长度,如果超出,数组将会进行扩容,以满足添加数据的需求。数组扩容实质上是通过私有的方法ensureCapacityInternal(int minCapacity) -> ensureExplicitCapacity(int minCapacity) -> grow(int minCapacity)来实现的,但在jdk1.8中,向用户提供了一个public的方法ensureCapacity(int minCapacity)使用户可以手动的设置ArrayList实例的容量,以减少递增式再分配的数量。此处与jdk1.6中直接通过一个公开的方法ensureCapacity(int minCapacity)来实现数组容量的调整有区别。

/*** 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 codeif (minCapacity - elementData.length > 0)grow(minCapacity);}//数组可被分配的最大容量;当需要的数组尺寸超过VM的限制时,可能导致OutOfMemoryErrorprivate static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;/*** 增加数组的容量,确保它至少能容纳指定的最小容量的元素量* @param minCapacity 期望的最小容量*/private void grow(int minCapacity) {// overflow-conscious codeint 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) // overflowthrow 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++;  // 将“修改统计数”+1int 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就讲完了。

就这么简单。

android 获取ArrayList的Capacity相关推荐

  1. android 获取已安装 错误代码,android获取手机已经安装的app信息

    Android获取手机已安装APP(系统/非系统) 效果图 主体代码 private ListView mlistview; private ListpackageInfoList; private ...

  2. android 获取图片上某一个文字位置_android 获取手机中的所有图片或某一目录下的图片方法...

    获取手机中的所有图片,并过滤获取某一目录下的图片.(注释掉的代码可以按照目录分组) private void getAllPhotoInfo() { new Thread(new Runnable() ...

  3. android获取到电信的手机号码,Android基站信息获取以及Sim卡相关信息获取

    概述: 本篇主要介绍Android获取基站信息的方式,除此之外,还有SIM卡相关字段获取,先介绍一些缩写的概念,后续更新代码的写法. 前言:之前有碰到一个需求,需要获取SIM卡的相关属性:IMSI号. ...

  4. android获取短信中心号

    android获取短信中心号: 使用读取短信的方式获取短信中心号,然后进行频率统计,获取次数最多的.代码经真机测试可以运行. 运行效果图: 源码0分下载url: http://download.csd ...

  5. Android获取系统相册图片选中地址,获取手机中的所有图片地址自定义相册

    一.获取手机中的值 1.首先在使用读写sd卡权限 2.获取手机中的所有图片: 注意代码中的getGalleryPhotos(getContentResolver()) 方法获取所有地址 获取所有图片地 ...

  6. Android获取正在运行的进程列表

    Android 获取正在运行的进程,下列方法暂时只能获取Android 7 及以下版本 三方库: implementation 'com.jaredrummler:android-processes: ...

  7. Android获取一周每一天的日期

    项目终于完事了,这几天挺闲的,想着写点东西,把项目中遇到的问题和解决方法总结一下.Android获取一周每一天的日期,就是给出这周某一天的日期计算出这周每一天的日期. 这里,我们把给的这一天设为dat ...

  8. Android 获取设备各种信息以及其它

    做手机开发,想必都希望获取手机号码吧,android中有一个类android.telephony.TelephonyManager提供这个功能. TelephonyManager tm = (Tele ...

  9. Android 获取通话记录、联系人

    文章目录 1.简介 2.代码结构 3.activity_main.xml 布局文件 4.AndroidManifest.xml 添加权限 5.MainActivity .java 功能文件 6.获取联 ...

最新文章

  1. Angular面试题三
  2. vs2005中关于masterpage,Theme,skin的一点总结
  3. Wtm携手LayUI -- .netcore 开源生态我们是认真的!
  4. c语言 函数的参数传递示例_isunordered()函数与C ++中的示例
  5. 中国内裤衬里行业市场供需与战略研究报告
  6. 请大家慎用联想笔记本的NOVO功能
  7. ps -ef|grep httpServer|grep -v grep|cut -c 9-15|xargs kill -9
  8. OpenCV stereo matching 代码
  9. javascript DOM操作
  10. Linux进程的管理与调度(三) -- Linux进程ID号
  11. Visio使用技巧总结
  12. DDC及EDID内容简介
  13. redis操作之迭代器 hscan
  14. 开心庄园html的代码,HTML第五章(示例代码)
  15. VR全景制作方法教程完整版
  16. 混频器 matlab,基于FPGA数字混频器的设计
  17. 中国软件业的机会——抓住机遇、挑战未来
  18. [软件工程 复习] 记录
  19. 付费入群怎么做_微信群怎么设置付费才可以进入
  20. 干货 | 深度理解数据采集与埋点,提高自主数据分析能力!

热门文章

  1. 编程语言的分类及其优缺点,Python标准输入与输出
  2. linux下FTP服务搭建(1)
  3. spoj 375 Query on a tree (树链剖分)
  4. 『奇葩问题集锦』Fedora ubuntu 下使用gulp 报错 Error: watch ENOSPC 解决方案
  5. NYOJ6——喷水装置(一)
  6. sqlite3-查看数据库
  7. 应用Strong Name保存.NET应用程序集
  8. Linux下mysql 5.x得到root密码后的另外一种利用方式
  9. android webview 加载进度和自定义404错误页面
  10. VScode中Python的交互式命令环境使用笔记