本人文科生,转行做java开发,目前在恶补计算机基础.这篇文章就是一个学习笔记,记录自己学习ArrayList的一些理解.我是模仿java的ArrayList的实现思路写的,java里有些native的方法是自己写的,有不正确的地方希望直接指出我会改正的,不必喷我,我目前就是小白.欢迎大家批评指正.
为了方便看 我将方法大致分了四类即 增 删 改 查.另外源码中的System.arraycopy方法是native方法,我都是自己实现的.我觉得学习ArrayList 主要就是学习如何自己实现这些本地方法.
public class MyArrayList extends AbstractList
implements List,RandomAccess,Cloneable,java.io.Serializable {
//------------------------------------全局变量------------------------------------------------
private static final long serialVersionUID = 4319787461277116437L;
/**
* Default initial capacity
* 初始化时给的容量,该容量只有在调用add方法时 检测到是使用无参构造初始化时才会分配10个空间,
* 含参构造不会使用到这个DEFAULT_CAPACITY
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* Shared empty array instance used for empty instances.
* 创建对象时如果指定容量为0 则使用该对象创建一个空数组
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* 用于给无参构造初始化
*/
private static final Object[] DEFAULT_EMPTY_ELEMENTDATA = {};
/**
* object类型的数组对象
*/
transient Object[] elementData;
/**
*
*用于记录数组里的元素的数量
*其实size也相当于一个指针,初始位置在0位置,每添加一个元素指针后移一位
*/
private int size;
//-------------------------------------构造方法--------------------------------------------
/**
* 带参构造函数
*
*/
public MyArrayList(int initialCapacity){
//如果参数大于0 就创建一个该容量大小的object类型的数组
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
//如果是0 就创建一个空数组
this.elementData = EMPTY_ELEMENTDATA;
} else {
//说明参数小于0 抛出参数不合法异常
throw new IllegalArgumentException(initialCapacity+"参数不合法");
}
}
/**
* 构建无参构造函数 默认生成一个空数组
*/
public MyArrayList(){
this.elementData = DEFAULT_EMPTY_ELEMENTDATA;
}
//------------------------------查找-----------------------------------------
/**
* 此方法用于查看数组里元素的数量
* @return 元素个数
*/
public int size(){
return size;
}
/**
*
* @return 空为true 非空为false
*/
public boolean isEmpty(){
return size == 0;
}
/**
* 返回指定元素的下标 从下标0开始查找 并返回找到的第一个该元素的下标
* 例如查找0元素的位置[5,0,-5,2,1,3,2,0] 返回值为1
* @param o
* @return
*/
public int indexOf(Object o){
if (o == 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;
}
/**
* 查看是否包含某个元素
* @param o
* @return
*/
public boolean contains(Object o){
//只要返回的下标值不是-1说明该数据存在
return indexOf(o) >= 0;
}
/**
* 从后往前找 返回第一个该元素的下标位置
* 例如查找0元素的位置[5,0,-5,2,1,3,2,0] 返回值为7
* @return
*/
public int lastIndexOf(Object o){
if(o == null){
for (int i = size-1; i >= 0; i--){
if (elementData[i] == null){
return i;
}
}
}else {
for (int i = size-1; i >= 0; i--){
if (o.equals(elementData[i])){
return i;
}
}
}
return -1;
}
// Positional Access Operations
@SuppressWarnings("unchecked")
E elementData(int index){
return (E) elementData[index];
}
/**
* 返回指定下标的元素
* @param index
* @return
*/
@Override
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
private void rangeCheck(int index) {
if (index >= size){
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
}
private String outOfBoundsMsg(int index){
return "index: "+index+", size: "+size;
}
//------------------------------新增元素------------------------------------
/**
* Appends the specified element to the end of this list.
* 往数组末尾添加元素
* 添加时的逻辑
* 1.调用ensureCaoacityInternal方法判断容量是否够用
* 2.在判断容量的时候去调用ensureExplicitCapacity方法判断是否需要扩容
* 3.在判断扩容的时候需要去确认初始化时有没有分配容量 注意即使分配的是0 也是不会去
* 调用 return Math.max(DEFAULT_CAPACITY,minCapacity);只有在调用无参构造时才会给默认分配10个容量
* 4.调用 ensureExplicitCapacity 判断是否需要调用grow进行扩容
* @param e
* @return
*/
public boolean add(E e){
//添加之前去调这个方法 确认数组长度够不够
//例如目前size是3 这一次添加需要去确认数组长度是不是大于等于4(长度至少是4才行),
ensureCapacityInternal(size+1);
//size对应元素下标 首次添加元素size是0
elementData[size] = e;
//每次添加完元素size加1
size++;
return true;
}
//minCapacity的值最小是1(数组为空时)
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData,minCapacity));
}
//calculateCapacity这个方法主要是判断在初始化时 有没有分配容量 如果没有会分配默认容量10
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULT_EMPTY_ELEMENTDATA){
//当初始化时没有给数组容量的时候 会给数组分配10个空间
//只有使用无参构造创建对象时才会进到这里
return Math.max(DEFAULT_CAPACITY,minCapacity);
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
//如果当前数组长度小于 需要的最小容量 需要扩容
if (minCapacity - elementData.length > 0){
grow(minCapacity);
}
}
/**
* The maximum size of array to allocate.
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
* 因为数组还包含一些额外的头信息在里面这些信息不超过8字节
*
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
//1.5倍扩容(右移运算)
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0){
newCapacity = minCapacity;
}
//当扩容后的newCapacity大于 2^31-1-8 时调用hugeCapacity
if (newCapacity - MAX_ARRAY_SIZE > 0){
newCapacity = hugeCapacity(minCapacity);
}
//新建一个数组容量为newCapacity 将旧数据放进去
Object[] newList = new Object[newCapacity];
for (int i = 0; i < elementData.length; i++) {
newList[i] = elementData[i];
}
将elementData 指向扩容后的数组
elementData = newList;
}
private static int hugeCapacity(int minCapacity){
if (minCapacity < 0){
//溢出了 所以数值小于0
throw new OutOfMemoryError();
}
//如果没有溢出
return (minCapacity > MAX_ARRAY_SIZE)
?Integer.MAX_VALUE
:MAX_ARRAY_SIZE;
}
/**
* 向指定位置插入数据
* @param index
* @param element
*/
public void add(int index,E element){
//1.校验位置合法性
rangeCheckForAdd(index);
//2.确认是否需要扩容
ensureCapacityInternal(size+1);
//3.把该位置以及其后面的元素全部后移一位
for (int i = size; i > index; i--) {
elementData[i] = elementData[i-1];
}
//4.插入指定位置
elementData[index] = element;
//5.容量加一
size++;
}
private void rangeCheckForAdd(int index){
if (index < 0 || index > size){
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
}
//-----------------------------删除指定元素-----------------------------------
/**
* 根据下标删除元素
* @param index
* @return
*/
public E remove(int index){
//1.参数合法性校验
rangeCheck(index);
//2.记录旧数据
E oldValue = elementData(index);
//3.检查index是不是指向最后一个元素,
//如果不是则需要移动元素
int numMoved = size-1-index;//numMoved=0表示index指向最后一个元素
if (numMoved > 0){
//将index位置到末尾的元素依次向前移动一位
//例如[0,1,2,3,4]如果删除元素0,则执行完数组为[1,2,3,4,4]
//所以需要将末尾多余的4置为null,以便于GC 并且保证打印数组时里面的元素是正确的
for (int i = index; i < size-1; i++) {
elementData[i] = elementData[i+1];
}
}
//如果numMoved=0说明删除的是末尾元素那么直接将末尾元素置为null就行
//否则执行完if的移动元素后,再将末尾元素置为null,同时数量减一
elementData[--size] = null;// clear to let GC do its work
return oldValue;
}
/**
* 根据元素删除
* @param o
* @return
*/
public boolean remove(Object o){
//1.判断元素是否为null
if (null == o){
for (int i = 0; i < size; i++) {
if (elementData[i] == null){
fastRemove(i);
return true;
}
}
}else {
for (int i = 0; i < size; i++) {
if (elementData[i] == o){
fastRemove(i);
return true;
}
}
}
return false;
}
private void fastRemove(int index) {
//1.检查是否index指向最后一个元素
int numMoved = size-1-index;
if (numMoved > 0){
//将index位置到末尾的元素依次向前移动一位
for (int i = index; i < size-1; i++) {
elementData[i] = elementData[i+1];
}
}
elementData[--size] = null;
}
//-------------------------------修改--------------------------------------
/**
* 修改指定位置上的元素
* @param index
* @param element
* @return
*/
public E set(int index,E element){
//1.参数校验
rangeCheck(index);
//2.记录旧数据
E oldValue = elementData(index);
//3.更改数据
elementData[index] = element;
return oldValue;
}
/**
* 此方法可以将未使用到的空间给裁剪掉
*/
public void trimToSize(){
modCount++;
//如果数组的容量大于当前数组里元素的数量 则需要裁剪
if (size < elementData.length){
elementData = (size == 0)
//如果数组里没有元素 那么就把数组置为空数组
? EMPTY_ELEMENTDATA
//否则需要裁剪
: doTrim(elementData,size);
}
System.out.println("裁剪后的数组长度-->"+elementData.length);
}
//数组裁切方法 源码使用的是native方法,这里可以自己实现
public static <T> T[] doTrim(T[] elementData, int size) {
Object[] newArray = new Object[size];
for (int i=0; i<size; i++){
newArray[i] = elementData[i];
}
return (T[]) newArray;
}
}