java数据结构03_ArrayList底层源码分析

ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长,类似于C语言中的动态申请内存,动态增长内存。

特点:查询效率高,增删效率低,线程不安全。

在这里插入图片描述

查看源码,我们可以看出ArrayList底层使用Object数组来存储元素数据。所有的方法,都围绕这个核心的Object数组来开展。

对ArrayList的操作,其实就是对数组的操作,下面我们来分析ArrayList的底层实现:

  • 私有属性:

ArrayList类只定义了两个私有属性:

public class ArrayList {
	// 存放元素的数组
	private Object elementData;
	// 存放元素的个数
	private int size;
}

很容易理解,elementData存储ArrayList内的元素,size表示它包含的元素的数量。

  • 构造方法:

ArrayList提供的构造器,可以构造一个默认初始容量为10的空列表、构造一个指定初始容量的空列表。

public class ArrayList {
// ...省略私有属性...
	// 无参构造方法,默认容量是10。
	public ArrayList() {
		this(10); 
	}
	// 带容量大小的构造函数
	public ArrayList(int initialCapacity) {
		// 如果参数非法,抛出异常
		if(initialCapacity < 0) {
			throw new IllegalArgumentException("initialCapacity: 不能为负数 "+
                    initialCapacity);
		}
		// 创建指定容量的数组
		this.elementData = new Object[initialCapacity];
	}
}
  • 添加元素方法:

我们知道,数组长度是有限的,而ArrayList是可以存放任意数量的对象,长度不受限制,那么它是怎么实现的呢?本质上就是通过定义新的更大的数组,将旧数组中的内容拷贝到新数组,来实现扩容。 ArrayList的Object数组初始化长度为10,如果我们存储满了这个数组,需要存储第11个对象,就会定义新的长度更大的数组,并将原数组内容和新的元素一起加入到新数组中,源码如下:

public class ArrayList {
// ...省略私有属性和构造方法...
	// 追加一个元素方法
	public boolean add(Object element) {
		// 1.检查数组是否需要扩容
		ensureCapacityInternal(size + 1);
		// 2.在数组末尾追加一个元素
		elementData[size] = element;
		// 3.数组元素个数累加
		size++;
		return true;
	}
	// 数组扩容方法
	private void ensureCapacityInternal(int minCapacity) {
		// 1.获取数组长度
		int oldCapacity = elementData.length;
		// 2.需求数组长度大于数组的原长度时,则证明需要扩容
		if(minCapacity > oldCapacity) {
			// 3.把数组空间扩展为原来的1.5倍(可自定义)
			int newCapacity = oldCapacity + oldCapacity >> 1;
			// 4.如果扩为1.5倍还不满足需求,直接扩为需求值
			if(newCapacity < minCapacity)
				newCapacity = minCapacity;
			// 5.定义一个更大的新数组(数组长度)
			Object[] newElementData = new Object[newCapacity];
			// 6.将旧数组中的内容拷贝到新数组
			System.arraycopy(elementData, 0, newElementData, 0, size);
			// 7.使原数组指向新数组,完成对elementData数组的扩容
			elementData = newElementData;
		}
	}
}
  • 获取元素方法:

ArrayList的get方法就比较简单了,先做index检查,然后执行访问操作。

public class ArrayList {
// ...省略私有属性和构造方法...
	// 根据索引获取元素
	public Object get(int index) {
		// 1.检查所有是否合法
		rangeCheck(index);
		// 2.根据索引获取元素
		return elementData[index];
	}
// 检查所有是否合法
	private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException("移除索引越界");
    }
}
  • 修改元素方法:

ArrayList的set方法和获取元素方法比较类似,先做index检查,然后执行修改操作。

public class ArrayList {
// ...省略私有属性和构造方法...
// 根据索引替换数组中的元素
	public Object set(int index, Object element) {
		// 1.检查所有是否合法
		rangeCheck(index);
		// 2.获取被替换的元素
		Object oldValue = elementData[index];
		// 3.替换元素
        elementData[index] = element;
        // 4.返回替换元素的值
        return oldValue;
	}
}
  • 插入元素方法:

将元素插入到列表中指定的位置,先检查传入的索引是否合法,然后判断数组是否需要扩容,接着将指定位置以及后续的元素向后移动一位,最后再插入元素。

public class ArrayList {
	// ...省略私有属性和构造方法...
	// 指定位置插入一个元素
	public void add(int index, Object element) {
		// 1.检查索引位置是否合法
		rangeCheckForAdd(index);
		// 2.检查是否需要扩容
		ensureCapacityInternal(size + 1);
		// 3.将指定位置以及后续的元素向后移动一位
		System.arraycopy(elementData, index,elementData, index + 1, size - index);
		// 4.指定位置插入元素
		elementData[index] = element;
		// 5.数组元素个数累加
		size++;
	}
	// 检查索引位置是否合法
	private void rangeCheckForAdd(int index) {
		if(index < 0 || index > this.size)
            throw new IndexOutOfBoundsException("插入索引越界");
    }
}
  • 移除元素方法:

根据索引来移除元素,首先先判断索引是否合法,然后将指定位置以及后续的元素向前移动一位,最后把数组中最后一个元素设置为null。

根据元素来移除元素,首先找到该元素在数组中所在的索引,如果没有找到则证明移除失败,如果找到则进行根据索引来移除元素的操作。

public class ArrayList {
	// ...省略私有属性和构造方法...
	// 根据索引移除数组元素
	public Object remove(int index) {
		// 1.检查索引位置是否合法
		rangeCheck(index);
		// 2.获取索引所在的元素
		Object oldValue = elementData[index];
		// 3.删除输出元素
		fastRemove(index);
		// 4.返回被删除的元素
		return oldValue;
	}
	// 根据元素移除数组元素
	public boolean remove(Object o) {
		// 因为集合中可以存放null,所以判断之前需判断元素是否为null
		if(o == null) {
			for (int index = 0; index < size; index++)
				// 判断元素是否为null
                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) {
		// 1.将指定位置后续的元素向前移动一位
		System.arraycopy(elementData,index+1,elementData,index,size-index-1);
		// 2.把数组最后一个元素设置为null,并数组元素个数减一
		elementData[--size] = null;
	}
}

ps:如需最新的免费文档资料和教学视频,请添加QQ群(627407545)领取。

发布了35 篇原创文章 · 获赞 0 · 访问量 365

猜你喜欢

转载自blog.csdn.net/zhoujunfeng121/article/details/104535454