JDK源代码学习系列04----ArrayList
JDK源代码学习系列04----ArrayList
1.ArrayList简单介绍
ArrayList是基于Object[] 数组的,也就是我们常说的动态数组。
它能非常方便的实现数组的添加删除等操作。
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable</span>
ArrayList支持泛型,它继承自AbstractList。实现了List、RandomAccess、Cloneable、java.io.Serializable接口。
List接口定义了列表必须实现的方法。
RandomAccess是一个标记接口,接口内未定义不论什么内容。
实现了Cloneable接口的类,能够调用Object.clone方法返回该对象的浅拷贝。
通过实现 java.io.Serializable 接口以实现序列化功能。
2.ArrayList成员变量
private transient Object[] elementData;//注意keyword transient
private int size;//实际size,不是容量
Javakeyword transient:是为了在序列化时保护对象的某些域不被序列化。在Java序列化Serializable具体解释中有所提及。
关于transient的具体内容稍后补上。
3.ArrayList构造函数
public ArrayList(int initialCapacity) {//为ArrayList初始化容量
super();
if (initialCapacity < 0)//若传入參数小于0。则报IllegalArgumentException的错误
throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
this.elementData = new Object[initialCapacity];//初始elementData数组的大小为此传入的參数
}
public ArrayList() {//ArrayList初始容量,即默认容量为10
this(10);
}
public ArrayList(Collection<?
extends E> c) {//把整个集合初始化给ArrayList.注意:该集合内的数据类型必须与ArrayList一致!!!!
elementData = c.toArray();
size = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)//!!这是jdk的一个bug,说是c.toArray()不一定返回的是Object类型
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);//调用的Arrays.copyOf()
}
4.ArrayList的成员函数
a.void trimToSize()
public void trimToSize() {//节约内存
modCount++;//此变量记录ArrayList被改变的次数 。!!
超级注意!
!。
int oldCapacity = elementData.length;
if (size < oldCapacity) {//!!size往往不等于elementData.length;elementData.length是数组的初始长度。size是实际内容的长度!!
elementData = Arrays.copyOf(elementData, size);
}
}
modCount变量是记录ArrayList被改变的次数。为什么须要这个变量呢?
ArrayList不是线程安全(异步)的。
这里会引出Fail-Fast机制:
ArrayList不是线程安全的,因此假设在使用迭代器的过程中有其它线程改动了map,那么将抛出ConcurrentModificationException,这就是所谓fail-fast策略。
这一策略在源代码中的实现是通过modCount域。modCount顾名思义就是改动次数。对ArrayList 结构的改动(长度的变化。添加,删除;赋值不是结构变化)都将添加这个值,那么在迭代器初始化过程中会将这个值赋给迭代器的expectedModCount。
在迭代过程中,推断modCount跟expectedModCount是否相等。假设不相等就表示已经有其它线程改动了ArrayList。
ArrayList中的mouCount是在他的父类Abstract中申明的。
protected transient int modCount = 0;
b.void ensureCapacity(int minCapacity)
public void ensureCapacity(int minCapacity) {
modCount++;
int oldCapacity = elementData.length;
if (minCapacity > oldCapacity) {
Object oldData[] = elementData;
int newCapacity = (oldCapacity * 3)/2 + 1;//若传入參数大于原容量,先扩为 1.5*原容量+1
if (newCapacity < minCapacity)
newCapacity = minCapacity;//若扩为 1.5*原容量+1 后还是小于传入的參数,则把传入的參数作为新容量
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
}
ArrayList须要扩容时至少都是扩为 1.5*原容量+1 ,若1.5*原容量+1 还是小于传入的參数,才把传入的參数作为新容量。
c.boolean contains(Object o)
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
d.int indexOf(Object o)
public int indexOf(Object o) {
if (o == null) {//定位是null也要定位的哦~
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
e.Object[] toArray() / <T> T[] toArray(T[] a)
调用的是Arrays.copyOf()
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());//若传入的数组长度小于size(ArrayList实际长度),则返回一个长度为size新的数组
System.arraycopy(elementData, 0, a, 0, size);//若传入数组长度相等,则把elementData复制进传入数组
if (a.length > size)//若传入的a的长度大于原本数组。则后面补null
a[size] = null;
return a;
}
f.E set(int index, E element)
set是直接替换掉该位置元素。而add是插入该位置,其余元素后移。
public E set(int index, E element) {
RangeCheck(index);//參数检查的方法~~一定要时刻注意參数检查哦~~
E oldValue = (E) elementData[index];
elementData[index] = element;
return oldValue;
}
g.boolean add(E e) / void add(int index, E element)
public boolean add(E e) {
ensureCapacity(size + 1); // add()时先扩容!
elementData[size++] = e;//!!写的非常好,size++的同一时候还完毕了在最后位置的赋值。之所以size++不会报边界溢出的错误是由于上面已经扩容了。
return true;
}
public void add(int index, E element) {
if (index > size || index < 0)throw new IndexOutOfBoundsException("Index: "+index+", Size: "+size);
ensureCapacity(size+1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,size - index);//!!把elementData的数据从index->末尾所有拷贝到从index+1開始。复制长度无size-index
elementData[index] = element;//相当于把传入的element插入到空出的位置,即原index
size++;
}
注意System.arracopy()方法!
。
h.E remove(int index) / boolean remove(Object o) / void fastRemove(int index)
public E remove(int index) {
RangeCheck(index);
modCount++;
E oldValue = (E) elementData[index];//得到须要返回的被remove掉的元素
int numMoved = size - index - 1;//复制时复制的长度。
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,numMoved);//把原数组从index+1-->末尾的数据拷贝到 index的位置,即相当于把原本index上的数据覆盖掉了。这样最后就空出了一个位置。
elementData[--size] = null; // 先把size减一,在把最后一赋值为null
return oldValue;
}
public boolean remove(Object o) {
if (o == null) {//!!推断是否为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;
}
private void fastRemove(int index) {//高速移除! 此方法跳过了边界检查这一步。且不会返回被移除的元素。
平时还是不要用哦~~
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,numMoved);
elementData[--size] = null; // Let gc do its work
}
i.void clear()
public void clear() {//把每一个都置为null,再设置size=0;
modCount++;
// Let gc do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
g.boolean addAll(int index, Collection<? extends E> c)
public boolean addAll(int index, Collection<?
extends E> c) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacity(size + numNew); // 扩容时传入的參数为:如今的实际长度+传入的集合的长度
int numMoved = size - index;
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew,numMoved);//把原数组从index-->末尾拷贝到 index+numNew-->末尾。即中间空出numNum的长度来为传入的集合做准备。
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
5.总结
a.ArrayList动态数组!
b.ArrayList 非线程安全,即 是异步的。 单线程才用ArrayList。
JDK源代码学习系列04----ArrayList的更多相关文章
- JDK源代码学习系列07----Stack
JDK源代码学习系列07----Stack 1.Stack源代码很easy ...
- JDK源代码学习系列05----LinkedList
JDK源代码学习系列05----LinkedList 1.LinkedList简单介绍 LinkedList是基于双向 ...
- JDK源代码学习系列03----StringBuffer+StringBuilder
JDK源代码学习系列03----StringBuffer+StringBuilder 因为前面学习了StringBuffer和StringBuilder的父类 ...
- Java小白集合源码的学习系列:ArrayList
ArrayList源码学习 本文基于JDK1.8版本,对集合中的巨头ArrayList做一定的源码学习,将会参考大量资料,在文章后面都将会给出参考文章链接,本文用以巩固学习知识. ArrayList的 ...
- JDK源代码学习-ArrayList、LinkedList、HashMap
ArrayList.LinkedList.HashMap是Java开发中非常常见的数据类型.它们的区别也非常明显的,在Java中也非常具有代表性.在Java中,常见的数据结构是:数组.链表,其他数据结 ...
- JDK源代码学习-基础类
一.概述 1.Java,是一套语言规范,例如规定了变量如何定义.控制语句如何写等,提供基本的语法规范.JDK是java自带的一套调用组件,是对基本java语法规范的进一步封装,jdk中都是使用java ...
- Java小白集合源码的学习系列:LinkedList
目录 LinkedList 源码学习 LinkedList继承体系 LinkedList核心源码 Deque相关操作 总结 LinkedList 源码学习 前文传送门:Java小白集合源码的学习系列: ...
- Java小白集合源码的学习系列:Vector
目录 Vector源码学习 Vector继承体系 Vector核心源码 基本属性 构造器 扩容机制 Enumeration 概述 源码描述 具体操作 Vector总结 Vector源码学习 前文传送门 ...
- JDK源码学习系列04----ArrayList
JDK源码学习系列04----ArrayList 1. ...
随机推荐
- Android短信监听实现,及Android4.4之后短信机制变更
前阵子公司有一个项目,简单的监听短信应用,功能只有如下两个: 1.监听短信并获取短信内容上传服务器: 2.从服务器获取短信内容,发送出去 按照传统的思路,监听短信我们有两种方式:第一种是使用广播 ...
- [Node.js] 04 - Event and Callback
回调函数 回调函数在完成任务后就会被调用,Node 使用了大量的回调函数,Node 所有 API 都支持回调函数. 异步读取文件的回调函数: var fs = require("fs&quo ...
- 中文数据解码报错 UnicodeDecodeError: 'gbk' codec can't decode bytes in position 2-3: illegal multibyte sequence
UnicodeDecodeError: 'gbk' codec can't decode bytes in position 2-3: illegal multibyte sequence 失败原因: ...
- Linux中 SonarQube代码质量管理平台安装
SonarQube是管理代码质量一个开源平台,可以快速的定位代码中潜在的或者明显的错误. SonarQube安装 1.环境准备 (1)sonarQube 下载地址https://www.sonarqu ...
- oracle 创建表同时添加注释
创建数据库表.添加注释的方法: create table WARNINGRECORD ( RecordID ) primary key not null ); comment on column WA ...
- SVM 核方法
在 SVM 中引入核方法便可使得 SVM 变为非线性分类器,给定非线性可分数据集 $\left \{ (x_i,y_i)\right\}_{i=1}^N$,如下图所示,此时找不到一个分类平面来将数据分 ...
- 7.17python
1.事件: # !/usr/bin/env python # !--*--coding:utf-8 --*-- # !@Time :2018/7/17 10:38 # !@Author TrueNew ...
- MFC 应用程序中使用管道代码示意
STARTUPINFO sinf = {0}; PROCESS_INFORMATION pinf = {0}; SECURITY_ATTRIBUTES sa = {0}; HANDLE hPipeOR ...
- ASP.NET 前端Ajax获取数据并刷新
控制器中↓ /// <summary> /// 根据ID来进行展示数据 /// </summary> /// <param name="instru_id&qu ...
- Linq多表联合查询,在View中绑定数据
Ⅰ→通过ViewData传递数据,不过需要新建一个类(用来存) NewClass(里面有表1的字段和表2的字段) public class JoinTab1_2 { public int ID { g ...