DEBUG ArrayList
1,ArrayList面试必问
说说ArrayList和LinkedList的区别?
ArrayList基于数组实现,LinkedList基于链表实现,不同的数据结构决定了ArrayList查询效率比较高,而LinkedList插入删除效率比较高,反过来就比较慢了。
ArrayList默认初始容量为多少?按照几倍来扩容?
10,1.5倍。
说说数组扩容的原理?
ArrayList扩容调用的是Array.copyof函数,把老数组遍历赋值给新数组返回。
说说ArrayList常见方法的时间复杂度?
- get方法通过下标获取元素,时间复杂度为O(1)
- add方法直接添加会添加到集合的尾部,时间复杂度为O(1)
- add方法通过下标添加到非尾部会引起数组的批量移动,时间复杂度为O(n),否则为O(1)
- remove方法通过下标删除非尾部元素引起数组批量移动,时间复杂度为O(n),反之则为O(1)
- remove方法根据对象删除需要遍历集合,时间复杂度为O(n),如果删除的为非尾部元素,会使数组批量移动,时间复杂度为O(n^2)
总之,通过下标操作的时间复杂度为O(1),如果触发了数组的批量移动,时间复杂度为O(n),如果通过对象操作需要遍历集合,时间复杂度已经为O(n),若同时触发了数组的移动,时间复杂度为O(n^2).
ArrayList和vector的区别
- 最大的区别在于线程是否安全
- 其次Vector是两倍扩容
- 最后就是在不指定大小的情况下,ArrayList容量初始化是在添加元素的时候,而Vector有一个无参构造器直接初始化为10
2,Debug ArrayList源码
由于1.7和1.8几乎没什么变化,本文以jdk1.8为例。
2.1 用Debug分析一个元素是如何add进ArrayList
编写测试用例,打上断点:
先分析构造函数如何初始化,关键步骤如下:
elementData是ArraList底层数组的实现,(ps:hashMap数组使用table命名)
DEFAULTCAPACITY_EMPTY_ELEMENTDATA表示默认的空数组,也就是说ArrayList在构造函数初始化时并不会进行底层数组的初始化。
给元素的添加打上断点,分析过程:
进入add方法内部:
public boolean add(E e) {
//确保内部容量,在元素添加进来前可能要进行扩容操作,size初始化为0,表示集合的长度
ensureCapacityInternal(size + 1); // Increments modCount!!
//添加元素,size自增
elementData[size++] = e;
return true;
}
进入ensureCapacityInternal方法内部:此时elementData为空,size+1=minCapacity=1
ensureExplicitCapacity:确保明确的能力
计算容量,calculateCapacity方法:
private static int calculateCapacity(Object[] elementData, int minCapacity) {
//判断数组是否为空,若为空,返回默认容量和最小容量的最大值,若不为空,返回最小容量
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
DEFAULT_CAPACITY默认容量为10:
继续分析,进入:
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));此时参数为10,也就是ArrayList的默认容量
private void ensureExplicitCapacity(int minCapacity) {
modCount++; //集合的修改次数
//如果最小容量减去数组长度大于0,进行扩容,此时最小容量为10,数组长度为0
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
核心扩容函数grow:(ps:HashMap中扩容函数为resize)
private void grow(int minCapacity) {
//oldCapacity:旧数组容量
int oldCapacity = elementData.length;
//新容量等于旧容量加上旧容量的一半,>>1相当于除以2(ArrayList扩容是1.5倍)
int newCapacity = oldCapacity + (oldCapacity >> 1);
//新容量小于最小容量,则赋值为最小容量,此时newCapacity等于0,minCapacity为10
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
//赋值给新数组
elementData = Arrays.copyOf(elementData, newCapacity);
}
数组复制Arrays.copyOf:
2.2 用Debug分析如何通过数组下标获取ArrayList元素
打上断点,debug:
首先进行范围检查,而后返回元素
2.3 用Debug分析如何通过数组下标删除一个元素
打上断点:
进入remove方法内部,
public E remove(int index) {
//下标范围检查
rangeCheck(index);
//修改次数自增
modCount++;
//保留当前删除元素的值,稍后返回
E oldValue = elementData(index);
//需要移动元素的个数
int numMoved = size - index - 1;
if (numMoved > 0)
//底层使用native方法,debug进不去。native方法:java调用其他语言的接口
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
//最后一位置空
elementData[--size] = null; // clear to let GC do its work
//返回删除元素的值
return oldValue;
}
2.4 用Debug分析如何通过对象删除一个元素
进入remove方法:
public boolean remove(Object o) {
//如果对象为空,则遍历ArrayList集合
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
//不为空,也遍历
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
进入fastRemove方法:
private void fastRemove(int index) {
modCount++;
//numMoved:需要移动数组的个数
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its wrk
}
2.5 用Debug分析向数组中间添加元素
进入add方法
public void add(int index, E element) {
//下标范围检查
rangeCheckForAdd(index);
//确保容量大小
ensureCapacityInternal(size + 1); // Increments modCount!!
//移动数组位置
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
//赋值给数组
elementData[index] = element;
//元素个数增加
size++;
}
关于System.arraycopy时间复杂度问题,在添加或者删除最后一个元素的时候不会触发数组的复制机制,时间复杂度为O(1),若是添加到数组中间,由于会触发数组的复制,时间复杂度为O(n)。对于删除元素同样,根据数组下标删除的情况下,删除尾部元素是不会触发数组的扩容机制的,若删除中间的元素,同样会触发数组的复制。若根据对象删除元素,由于本身遍历到对象的时间复杂度为O(n),删除元素后再对数组进行重组,所以时间复杂度为O(n^2)。
DEBUG ArrayList的更多相关文章
- MAT使用--转
原文地址: [1]http://ju.outofmemory.cn/entry/172684 [2]http://ju.outofmemory.cn/entry/129445 MAT使用入门 MAT简 ...
- 韩顺平Java(持续更新中)
原创上课笔记,转载请注明出处 第一章 面向对象编程(中级部分) PDF为主 1.1 IDEA 删除当前行,ctrl+y 复制当前行,ctrl+d 补全代码,alt+/ 添加或者取消注释,ctrl+/ ...
- jmeter sampler maven项目排错记
eclipse 创建的maven项目,引入jar包之后出现红色叹号,一直找不到原因,连main方法都无法运行,提示找不到类: 错误: 找不到或无法加载主类 soapsampler.SoapSample ...
- JAVA基础学习之IP简述使用、反射、正则表达式操作、网络爬虫、可变参数、了解和入门注解的应用、使用Eclipse的Debug功能(7)
1.IP简述使用//获取本地主机ip地址对象.InetAddress ip = InetAddress.getLocalHost();//获取其他主机的ip地址对象.ip = InetAddress. ...
- Java中ArrayList相关的5道面试题
本文参考了 <关于ArrayList的5道面试题 > 1.ArrayList的大小是如何自动增加的? 这个问题我想曾经debug过并且查看过arraylist源码的人都有印象,它的过程是: ...
- C#之ArrayList
using System.Collections; 新建: ArrayList list = new ArrayList(); 添加元素: int a = 1; list.Add(a); 遍历: fo ...
- Handler 接收Parcelable ArrayList时返回空的错误
遇到一个问题,从handler 接收的Parcelable ArrayList返回空,调试发现这个arraylist生成的时候是有值的,传到handler就没值了 赋值的代码 new Thread(n ...
- 【转】Java如何克隆集合——深度拷贝ArrayList和HashSet
原文网址:http://blog.csdn.net/cool_sti/article/details/21658521 原英文链接:http://javarevisited.blogspot.hk/2 ...
- Java入门(7)——循环和debug 调试
循环: while 循环: 格式: int i = 0; ① //初始化条件 while(i < 10) { ② //判断条件 System.out.println(i); ④ //循环 ...
随机推荐
- 09.DRF-ModelSerializer
四.模型类序列化器ModelSerializer 如果我们想要使用序列化器对应的是Django的模型类,DRF为我们提供了ModelSerializer模型类序列化器来帮助我们快速创建一个Serial ...
- 分布式系统框架Spring+Redis+SSO视频课程
1.视频讲解的参看博客 这应该是第一个简单的分布式系统soa入门的基础,视频中对sao面向服务编程讲解的很透彻,第redis缓存讲解的也比较清楚,讲解了sso单点登录使用token的方式,还有cas实 ...
- 基于MarkDown和Github图床以及SourceTree的一站式文章编辑和发布
标题: 基于MarkDown和Github图床以及SourceTree的一站式文章编辑和发布 作者: 梦幻之心星 sky-seeker@qq.com 标签: [MarkDown,Github,图床,S ...
- Python3-hashlib模块-加密算法之安全哈希
Python3中的hashlib模块提供了多个不同的安全哈希算法的通用接口 hashlib模块代替了Python2中的md5和sham模块,使用这个模块一般分为3步 1.创建一个哈希对象,使用哈希算法 ...
- Docker 快速入门(一)- 情况介绍和安装
欢迎您! 很高兴您想学习 Docker . 这个页面包含了如何开始使用 Docker 的循序渐进的说明. Docker 快速入门培训模块教你如何: 设置 Docker 环境(在本页) 构建并运行您的镜 ...
- 【转载】提高访问 github 的速度
原文地址:https://www.cnblogs.com/liuchao888/p/11733996.html 工具地址:http://tool.chinaz.com/dns?type=1&h ...
- 小技巧:如何快速开启一个静态 HTTP 服务?
静态 HTTP 服务的几个用途: 静态网页的 HTTP 服务,以访问浏览 如:生成的文档.博客等 公开文件的 HTTP 服务,以访问下载 如:分享的文档.安装包等 以下会介绍目前我了解的方式中,最推荐 ...
- Java BigDecimal和double BigDecimal类
BigDecimal类 对于不需要任何准确计算精度的数字可以直接使用float或double,但是如果需要精确计算的结果,则必须使用BigDecimal类,而且使用BigDecimal类也可以进行大数 ...
- MyEclipse中项目利用不同版本JDK编译注意点
一.发现问题本地用jdk.1.6编译,可服务器上市jdk1.5,其他服务器甚至是jdk1.4,不少语法和特性不一样.该服务器的jdk版本一般不现实因为是还有许多其他项目只有将本地安装上jdk1.5. ...
- Js中各种类型的变量在if条件中是true还是false
如果操作数是一个对象,返回true如果操作数是一个空字符串,返回false如果操作数是一个非空字符串,返回true如果操作数是数值0,返回false如果操作数是任意非0数值(包括Infinity),返 ...