基于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 ...
随机推荐
- Java Reflection(getXXX和getDeclaredXXX)
package com.sunchao.reflection; public class Person { private int age ; private String name; public ...
- properties文件中中文不能显示或者中文乱码
1.properties 文件中文乱码问题 鼠标“右击”文件 => Resource => Text file encoding => UTF-8 2.properties 文件解析 ...
- 原生 JS 实现一个瀑布流插件
更好的阅读体验,点击 原文地址 瀑布流布局中的图片有一个核心特点 -- 等宽不定等高,瀑布流布局在国内网网站都有一定规模的使用,比如pinterest.花瓣网等等.那么接下来就基于这个特点开始瀑布流探 ...
- scrapy_Response and Request
scrapy中重要的两个类是什么? Requests.Response 什么是Requests? 网页下载 有哪些参数? url callback headers # 头部信息 cookie ...
- rem与@media 的优缺点
首先: 如果我们在做单独移动端网站或者app的时候 我建议 使用 rem ; 他能让我们在手机各个机型的适配方面:大大减少我们代码的重复性,是我们的代码更兼容. 下面两个图一个调试在常用的机 ...
- 安卓和IOS兼容问题
点击穿透 click延迟 scroll元素临界的bug android screen.w/h 不准 rem不准 scroll时动画失效 animate回调 最小字号限制 不同机型全屏自适应 andro ...
- scala 小结(一)
Scala 是什么?(What is scala?) 引用百度百科对于scala的定义: Scala是一门多范式的编程语言,一种类似java的编程语言,设计初衷是实现可伸缩的语言.并集成面向对象编 ...
- JavaWeb项目架构之Kafka分布式日志队列
架构.分布式.日志队列,标题自己都看着唬人,其实就是一个日志收集的功能,只不过中间加了一个Kafka做消息队列罢了. kafka介绍 Kafka是由Apache软件基金会开发的一个开源流处理平台,由S ...
- PyCharm配置autopep8,自动格式化Python代码
1. 关于PEP 8 PEP 8,Style Guide for Python Code,是Python官方推出编码约定,主要是为了保证 Python 编码的风格一致,提高代码的可读性. 官网地址:h ...
- 将常用的Android adb shell 命令行封装为C#静态函数
更多原创测试技术文章同步更新到微信公众号 :三国测,敬请扫码关注个人的微信号,感谢! 简介:adb命令是常用的Android命令行,自动化.代码调试.手工排查问题都会用的到,这里将常用的一些命令行封装 ...