基于JDK1.8的ArrayList剖析
前言
本文是基于JDK1.8的ArrayList进行分析的。本文大概从以下几个方面来分析ArrayList这个数据结构
- 构造方法
- add方法
- 扩容
- remove方法
(一)构造方法
/**
* Constructs an empty list with the specified initial capacity.
*
* @param initialCapacity the initial capacity of the list
* @throws IllegalArgumentException if the specified initial capacity
* is negative
*/
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);
}
} /**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
总所周知,ArrayList底层是数组
我们先看第二个构造方法,即无参构造方法(第22行),将默认空数组赋值给Object数组。这一段执行完,就在堆中分配了一段空的数组。
我们再看第一个构造方法,有参构造方法。
/**
* 加入我们有这么一段代码
*/
List<String> list = new ArrayList<>(10);
其构造方法有三个分支,initialCapacity大于0,等于0,小于0。我们直接看大于0,其他两个看代码就懂了。
他会直接构造一个以initialCapacity为大小的Object数组。
(二)add方法
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
size这个字段为数组的长度。然后进入ensureCapacityInternal这个方法。
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
} ensureExplicitCapacity(minCapacity);
}
可以看出来,第二行是判断是否位第一次add,也就是初始化。如果数组位空,那么DEFAULT_CAPACITY就是为10。
初始化完成,也就是开辟一个大小为10的Object数组。
private void ensureExplicitCapacity(int minCapacity) {
modCount++; // overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
minCapacity是list里面元素的个数,比如第一次放,也就是10,elementData.length也就是数组的长度,也就是0。
那么减法结果就为 10,不成立就不扩容,如果成立就扩容。
elementData[size++] = e;
然后返回add方法,执行这一句,至此,add方法就执行完毕了.接下来我们看下扩容的代码。
(三)扩容
/**
* 我们假设第一次扩容,list数组容量为10,再次add那么传过来的minCapacity为11
* oldCapacity为10
* newCapacity为 10 + (10 / 2) = 15 也就是扩容1.5倍
* 有两个if的极值判断,看代码很简单就懂了
* 然后执行Arrays.copy方法
*/
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
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);
}
我看了下Arryas的copy方法,也不难,大家可以对照着源码自己去看一下。
至此ArrayList的add方法就介绍完了,grow方法也搞定了。
(四)remove方法
/**
* remove方法总共有两个,一个是按照index删除,一个是按照元素删除,大同小异
* 下面说按照index删除,看注释
*/
public E remove(int index) {
//首先检查是否越界
rangeCheck(index); modCount++;
E oldValue = elementData(index); int numMoved = size - index - 1;
//把后面的全部移到前面一位,System.arraycopy是一个native方法
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
//然后最后一位置为空值,gc会自动回收
elementData[--size] = null; // clear to let GC do its work return oldValue;
}
remove方法很简单,直接看我写的注释即可。
(五)总结
写了两个多小时,我也醉了。主要我在调试eclipse的debug,我的那个有点问题,eclipseEE版本就没有问题。
其实我有一点疑惑,希望大佬能够给我解答以下。
初始化构造方法那里,并没有给size赋值,也就是为null值,不能直接输出或者使用(会报错,已实验)
但调用list.size()方法的时候,返回的确是0,没有报错,我debug也没看出来什么时候赋值了。
大佬看到这篇文章,希望可以解答下。谢谢。
-------------------------------华丽分割线----------------------------
好累啊
基于JDK1.8的ArrayList剖析的更多相关文章
- Java集合(四)--基于JDK1.8的ArrayList源码解读
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess ...
- Java集合基于JDK1.8的ArrayList源码分析
本篇分析ArrayList的源码,在分析之前先跟大家谈一谈数组.数组可能是我们最早接触到的数据结构之一,它是在内存中划分出一块连续的地址空间用来进行元素的存储,由于它直接操作内存,所以数组的性能要比集 ...
- ConcurrentHashMap基于JDK1.8源码剖析
前言 声明,本文用的是jdk1.8 前面章节回顾: Collection总览 List集合就这么简单[源码剖析] Map集合.散列表.红黑树介绍 HashMap就是这么简单[源码剖析] LinkedH ...
- 基于JDK1.8的LinkedList剖析
之前写了一篇ArrayList,那么今天就写一篇他的姊妹篇,LinkedList. 众所周知,ArrayList底层数据是数组,可以在O(1)的时间内get到数据,但删除和插入就要O(n)时间复杂度. ...
- 基于jdk1.8的ArrayList源码分析
前言ArrayList作为一个常用的集合类,这次我们简单的根据源码来看看AarryList是如何使用的. ArrayList拥有的成员变量 public class ArrayList<E> ...
- Java -- 基于JDK1.8的ArrayList源码分析
1,前言 很久没有写博客了,很想念大家,18年都快过完了,才开始写第一篇,争取后面每周写点,权当是记录,因为最近在看JDK的Collection,而且ArrayList源码这一块也经常被面试官问道,所 ...
- ArrayList的实现细节(基于JDK1.8)
ArrayList是我们经常用到的一个类,下面总结一下它内部的实现细节和使用时要注意的地方. 基本概念 ArrayList在数据结构的层面上讲,是一个用数组实现的list,从应用层面上讲,就是一个容量 ...
- Java集合类源码解析:HashMap (基于JDK1.8)
目录 前言 HashMap的数据结构 深入源码 两个参数 成员变量 四个构造方法 插入数据的方法:put() 哈希函数:hash() 动态扩容:resize() 节点树化.红黑树的拆分 节点树化 红黑 ...
- 基于JDK1.8的LinkedList源码学习笔记
LinkedList作为一种常用的List,是除了ArrayList之外最有用的List.其同样实现了List接口,但是除此之外它同样实现了Deque接口,而Deque是一个双端队列接口,其继承自Qu ...
随机推荐
- APIs
应用程序接口(application programe interfaces)
- Angular 4+ HttpClient
个人博客迁移至 http://www.sulishibaobei.com 处: 这篇,算是上一篇Angular 4+ Http的后续: Angular 4.3.0-rc.0 版本已经发布
- VS code注释快捷键
注释: 先CTRL+K,然后CTRL+C 取消注释: 先CTRL+K,然后CTRL+U
- 关于Serializable的serialVersionUID
在实现了Serializable接口的class中,需要声明一个long serialVersionUID,用来标明当前class的版本号,但很多人在编程时,总是不原意去声明这个serialVersi ...
- beetl 配置多视图解析器
如下配置,指定了三个视图解析器,一个用于beetl页面渲染,一个用于cms,采用了beetl技术,另外一个一些遗留的页面采用jsp <bean name="beetlConfig&qu ...
- 苹果内购服务器验证之receipt返回多组in_app思考
最近有部分用户反映,苹果内购充值失败,经过测试总结有几个关键点出现问题 1.app购买成功苹果没有返回票据,属于票据遗漏(取决于苹果服务器的响应状况),只能客户端进行监听刷新等处理 2.app连续购买 ...
- mybatis配置多个数据源事务(Transaction)处理
当mybatis配置文件中只有一个数据源的时候,按照正常的事务注解形式@Transaction是没有问题的,但是当配置文件中有多个数据源的时候发现事务不起作用了,怎么解决这个问题呢?看下面的案例:
- linux_DNS
linux其配置文件 : /etc/resolv.conf nameserver 223.5.5.5 nameserver 223.6.6.6 # 这两个解析地址为阿里云解析地址,格式也是这样 什么是 ...
- java打包成jar文件
JAR包是Java中所特有一种压缩文档,其实大家就可以把它理解为.zip包.当然也是有区别的,JAR包中有一个META-INF\MANIFEST.MF文件,当你找成JAR包时,它会自动生成.JAR包是 ...
- vc++调用web服务传输文件
bool webService::UploadFile(LPWSTR appKey, LPWSTR fileName, const int len, unsigned char * buff) { t ...