java ArrayList 实现
关于ArrayList的实现和原理,原文出处:http://www.cnblogs.com/ITtangtang/p/3948555.html
我觉得他写的非常好,真的很好.
做一个记录和总结吧
public class arraylist<E> { /**
* 存放集合的元素
*
*/
private transient Object[] elementData;
/** 元素的大小 */
private int size;
定义了一个泛型类,一个object的数组和一个私有变量来记录该集合的元素数量,原文多了一个私有变量,我也不知道干嘛用的,作者也没解释也没提及到,我没使用也没事
/**
* 根据指定大小初始化
* @param initialCapacity
*/
public arraylist(int initialCapacity){
super();
if(initialCapacity<=0){
//抛异常
throw new IllegalArgumentException("初始化参数不能小于0");
}else{
//初始化数组
this.elementData=new Object[initialCapacity];
}
}
/**
* 默认初始化
*/
public arraylist(){
this(10);
}
/**
* 根据一个集合类初始化
* @param c 一个必须继承了Collection接口的类
*/
public arraylist(Collection<? extends E> c){
//初始化
elementData=c.toArray();
size=elementData.length;
//如果不是任意类型的数组就转换Objec类型
if (elementData.getClass() != Object[].class){
elementData=Arrays.copyOf(elementData,size, Object[].class);
}
}
3个初始化方法,根据默认大小进行数组的初始化,给定大小初始化和传递一个继承了Collection集合接口的类进行转换赋值初始化
/**
* 扩容集合
* @param minCapacity
*/
public void ensureCapacity(int minCapacity){
/** 当前数组的大小 */
int oldCapacity = elementData.length;
if (minCapacity > oldCapacity) {
/**
* oldData 虽然没有被使用,但是这是关于内存管理的原因和Arrays.copyOf()方法不是线程安全
* oldData在if的生命周期内引用elementData这个变量,所以不会被GC回收掉
* 当Arrays.copyOf()方法在把elementData复制到newCapacity时,就可以防止新的内存或是其他线程分配内存是elementData内存被侵占修改
* 当结束是离开if,oldData周期就结束被回收
*/
Object oldData[] = elementData;
int newCapacity = (oldCapacity * 3)/2 + 1; //增加50%+1
if (newCapacity < minCapacity)
newCapacity = minCapacity;
//使用Arrays.copyOf把集合的元素复制并生成一个新的数组
elementData = Arrays.copyOf(elementData, newCapacity);
}
}
这是一个核心的方法,集合的扩容,其实是对数组的扩容,minCapacity集合的大小,进行对比判断是否应该进行扩容,使用了Arrays.copyOf()方法进行扩容,
原文有进行详细的解释,这个方法把第一个参数的内容复制到一个新的数组中,数组的大小是第二个参数,并返回一个新的数组,关于oldData的变量上文有详细的注释
/**
* 检查索引是否出界
* @param index
*/
private void RangeCheck(int index){
if(index > size || index < 0){
throw new IndexOutOfBoundsException("下标超出,Index: " + index + ", Size: " +size);
}
}
一个下标的检索是否出 1 /**
* 添加元素
* 将指定的元素添加到集合的末尾
* @param e 添加的元素
* @return
*/
public boolean add(E e){
ensureCapacity(size+1);
elementData[size]=e;
size++;
return true;
}
添加元素,先进行扩容,在赋值,然后元素加一,注意 size+1 字段size并没有加一,这里进行的是算术的运算,所以在后面才需要进行自增
/**
* 添加元素
* 将元素添加到指定的位置
* @param index 指定的索引下标
* @param element 元素
* @return
*/
public boolean add(int index, E element){
RangeCheck(index);
ensureCapacity(size+1);
// 将 elementData中从Index位置开始、长度为size-index的元素,
// 拷贝到从下标为index+1位置开始的新的elementData数组中。
// 即将当前位于该位置的元素以及所有后续元素右移一个位置。
System.arraycopy(elementData, index, elementData, index+1, size-index);
elementData[index]=element;
size++;//元素加一
return true;
}
这里不同的是 System.arraycopy(elementData, index, elementData, index+1, size-index);
这是一个c的内部方法,详细的原文有解释,这里就不说了,这个也是整个ArrayList的核心所在,也Arrays.copyOf()的内部实现原理
/**
* 添加全部元素
* 按照指定collection的迭代器所返回的元素顺序,将该collection中的所有元素添加到此列表的尾部。
* @param c
* @return
*/
public boolean addAll(Collection < ? extends E>c){
Object[] newElement=c.toArray();
int elementLength=newElement.length;
ensureCapacity(size+elementLength);
//从newElement 0的下标开始,elementLength个元素,elementData size的下标
System.arraycopy(newElement, 0, elementData, size, elementLength);
size+=elementLength;
return elementLength!=0;
}
基本上其他方法都只是根据不同的情况进行不同的处理,比如通过接口把数据对象传递进来然后获取长度进行扩容,在把数据使用System,arraycopy复制到新的数组中
/**
* 指定位置,添加全部元素
* @param index 插入位置的下标
* @param c 插入的元素集合
* @return
*/
public boolean addAll(int index, Collection<? extends E> c){
if(index > size || index < 0){
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " +size);
}
Object[] newElement=c.toArray();
int elementLength=newElement.length;
ensureCapacity(size+elementLength);
int numMoved=size-index;
//判断插入的位置是否在数组中间
if(numMoved>0){
//把index插入位置的后面的所有元素往后移
//elementData index下标开始的numMoved个元素插入到elementData 的index+elementLength位置
System.arraycopy(elementData, index, elementData, index+elementLength, numMoved);
}
//把newElement里从0开始的elementLength个元素添加到elementData index开始的位置
System.arraycopy(newElement, 0, elementData, index, elementLength);
size += elementLength;
return elementLength != 0;
} /**
* 指定下标赋值
* @param index
* @param element
* @return
*/
public E set(int index,E element){
RangeCheck(index);
E oldElement=(E)elementData[index];
elementData[index]=element;
return oldElement;
} /**
* 根据下标取值
* @param index
* @return
*/
public E get(int index){
RangeCheck(index);
return (E)elementData[index];
} /**
* 根据下标移除元素
* @param index
*/
public E remove(int index){
RangeCheck(index);
E oldElement=(E)elementData[index];
/** 移除的下标后面的元素数量 */
int numMoved=size-index-1;
//如果在数组范围内就进行移动
if(numMoved>0)
System.arraycopy(elementData, index+1, elementData, index, numMoved);
//移除
elementData[--size]=null;
return oldElement;
} /**
* 根据元素移除
* @param obj
* @return
*/
public boolean remove(Object obj){
//Arraylist允许存放null,所以也要进行判断处理
if(obj==null){
for(int index=0;index<size;index++){
if(elementData[index]==null){
remove(index);
return true;
}
}
}else{
for(int index=0;index<size;index++){
if(obj.equals(elementData[index])){
remove(index);
return true;
}
}
}
return false;
} /**
* 根据下标移除指定范围内的元素
* @param fromIndex 开始
* @param toIndex 结束
*/
protected void removeRange(int fromIndex, int toIndex){
RangeCheck(fromIndex);
RangeCheck(toIndex);
//要移动的元素数
int numMoved = size - toIndex;
//把toIndex后面的元素移动到fromIndex
System.arraycopy(elementData, toIndex, elementData, fromIndex, numMoved);
//要移除的元素数量
int newSize=size-(toIndex-fromIndex);
while(size!=newSize){
elementData[--size]=null;
}
} /**
* 把数组容量调整到实际的容量
*/
public void trimToSize(){
int leng=elementData.length;
if(size<leng){
Object[] old=elementData;
elementData=Arrays.copyOf(elementData, size);
}
}
/**
* 把集合元素转换成数组
* @return
*/
public Object[] toArray(){
return Arrays.copyOf(elementData, size);
} public <T>T[] toArray(T[] a){
if(a.length<size){
return (T[]) Arrays.copyOf(elementData,size, a.getClass());
}
//把集合元素复制到a数组中
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size){
for(int index=size;index<a.length;index++){
a[index] = null;
}
}
return a;
}
基本上都是对数组进行操作和使用c的方法进行赋值移动等,详细的可以查看原文,原文中除了那个私有变量外也没多少问题,代码可以完美运行,这李要注意的和难点就会是System,arraycopy和Arrayist.copy()这2个方法
和在扩容方法里oldData这个变量的使用,这个变量真的很好,一开始我也不知道为什么要这么使用,在原文的末尾会进行解释。
java ArrayList 实现的更多相关文章
- Java ArrayList、Vector和LinkedList等的差别与用法(转)
Java ArrayList.Vector和LinkedList等的差别与用法(转) ArrayList 和Vector是采取数组体式格式存储数据,此数组元素数大于实际存储的数据以便增长和插入元素,都 ...
- 浅析 java ArrayList
浅析 java ArrayList 简介 容器是java提供的一些列的数据结构,也可以叫语法糖.容器就是用来装在其他类型数据的数据结构. ArrayList是数组列表所以他继承了数组的优缺点.同时他也 ...
- Java ArrayList中对象的排序 (Comparable VS Comparator)
我们通常使用Collections.sort()方法来对一个简单的数据列表排序.但是当ArrayList是由自定义对象组成的,就需要使用comparable或者comparator接口了.在使用这两者 ...
- Java ArrayList源码剖析
转自: Java ArrayList源码剖析 总体介绍 ArrayList实现了List接口,是顺序容器,即元素存放的数据与放进去的顺序相同,允许放入null元素,底层通过数组实现.除该类未实现同步外 ...
- Java ArrayList 源代码分析
Java ArrayList 之前曾经参考 数据结构与算法这本书写过ArrayList的demo,本来以为实现起来都差不多,今天抽空看了下jdk中的ArrayList的实现,差距还是很大啊 首先看一下 ...
- Java ArrayList trimToSize()
前几天看了Java ArrayList,没有明白trimToSize()这个方法是什么意思,所以看了一下源码并且debug一下自己的一个例子,明白了其中的含义.贴在这里. ArrayList al = ...
- jdk 1.8下 java ArrayList 添加元素解析
转载请注明http://www.cnblogs.com/majianming/p/8006452.html 有人问我,java ArrayList底层是怎么实现的?我就回答数组,他再问我,那它是怎么实 ...
- Java ArrayList【笔记】
Java ArrayList[笔记] ArrayList ArrayList基本结构 ArrayList 整体架构比较简单,就是一个数组结构 源码中的基本概念 index 表示数组的下标,从 0 开始 ...
- JAVA - ArrayList是否会越界?
JAVA - ArrayList是否会越界? ArrayList并发add()可能出现数组下标越界异常. ArrayList是实现了基于动态数组的数据结构. LinkedList是基于链表的数据结构 ...
- 【简易版】Java ArrayList(增删改查)
1.什么是ArrayList ArrayList就是传说中的动态数组,用MSDN中的说法,就是Array的复杂版本,它提供了如下一些好处: (1)动态的增加和减少元素 (2)实现了ICollectio ...
随机推荐
- Angular指令2
scope Create a new scope for this directive rather than inheriting the parent scope. controller Crea ...
- .Net Core Linux centos7行—vscode开发,linux部署运行
前面搭建好啦linux运行环境,下面搭建windows下的开发环境.并完成调试 参考地址:https://www.microsoft.com/net/core#windows. 按照步骤来就好.安装. ...
- deepin 15.3 安装数据库MariaDB10.0
直接终端执行安装命令即可 sudo apt-get install mariadb-server -y 查询DB编码 #进入数据库 mysql -uroot -p #显示默认编码 show varia ...
- 神经网络和Deep Learning
参考资料: 在线免费书籍 http://neuralnetworksanddeeplearning.com/chap1.html Chapter 1 1. perceptron 感知机 it's a ...
- 配置linux----------------ip
在终端中输入 vi /etc/sysconfig/network-scripts/ifcfg-eth0 =================================== DEVICE=" ...
- IntelliJ IDEA For Mac 快捷键
Mac键盘符号和修饰键说明 ⌘ Command ⇧ Shift ⌥ Option ⌃ Control ↩︎ Return/Enter ⌫ Delete ⌦ 向前删除键(Fn+Delete) ↑ 上箭头 ...
- 戴尔OMSA 使用指南
戴尔的OMSA我们已经安装完成,但是没有web页面.因此我们目前想要看到服务器的相关硬件信息就需要使用命令行的方式获取了.那么,这里就介绍如何使用命令获取服务器硬件信息. 下面是我监控一些硬件信息的命 ...
- .net core
- python程序一直在后台运行的解决办法
刚写了个python程序,要一直在后台运行,即使断开ssh进程也在,下面是解决办法: 假如Python程序为test.py 编写shell脚本start.sh #!/bin/bash python t ...
- mysql问题
mysql x64,由于没有msi版,因此,在使用上可能会出现一些问题. 一. 配置环境变量: 只需把mysql/bin文件夹地址完整地添加到Path中,用“;”与前面的参数隔开.需注意,mysql ...