您的位置:首页 > 其它

ArrayList源码详解(常用方法源码分析)及 手动实现ArrayList

2019-03-03 17:56 591 查看

一、ArrayList详解

ArrayList是一个动态可变的数组。

特点:

  • 元素可以重复
  • 元素可以为null
  • 数据插入有序

常用方法介绍:

  • int size(); //集合中存储元素的个数
  • boolean isEmpty(); // 判断集合是否为空 ,返回false:不为空。返回true:为空
  • boolean contains(object o); // 判断当前集合是否包含object对象
  • Iterator iterator() ; //迭代器,返回iterator实例
  • Object[] toArray(); //将集合转换成数组
  • T[] toArray(T[] a); //将集合转换成数组:
  • boolean add; //添加元素
  • boolean remove(Object o) //删除元素
  • boolean containsAll(Collection<?> c) //判断入参集合是否属于当前集合
  • boolean containsAll(Collection<? extends E> c) //对该集合添加新的子集合形成新的集合
  • boolean containsAll(int index,Collection<? extends E> c)//在指定位置添加新的子集合形成新的集合
  • void clear(); //将集合内所有的数字置为null
  • boolean equals(Object o); //判断是否相等
  • E get(int index); //通过指定位置来获取元素
  • int indexOf(Object o); //判断当前元素在集合中的位置, 从前往后找
  • int lastIndexOf(Object o); //判断当前元素在集合中的位置。从后往前找
  • list subList(int fronIndex,int toIndex ) //找当前集合的子集,左闭右开,不包含右边的位置

JDK1.8源码分析:

  • 继承关系
public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable
//ArrayList继承AbstractList,父类中对部分接口进行实现,实现了list接口提供的方法,Serializable说明该类能被序列化
  • 底层数据结构
    数组
  • 基本属性和默认值
private static final long serialVersionUID = 8683452581122892189L;//与序列化有关的ID
private static final int DEFAULT_CAPACITY = 10;  //默认数组容量大小为10
private static final Object[] EMPTY_ELEMENTDATA = {};  //默认数组为空
transient Object[] elementData;          //存储元素的object类型的数组
private int size;  //记录集合元素的个数
  • 构造函数
1.带有一个参数(指定初始容量)的构造函数,
public ArrayList(int initialCapacity) {     //校验初始容量
if (initialCapacity > 0) { //new一个新的数组,容量为当前传入的大小
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
2.无参构造函数,构造一个具有初始容量为10的空列表
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
3.构造一个包含指定集合的元素的列表
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray(); //转换成数组
if ((size = elementData.length) != 0) {
if (elementData.getClass() != Object[].class)  //对element类型的数组进行强转
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
this.elementData = EMPTY_ELEMENTDATA; //重置为空数组
}
}
  • 增长方式
通过grow方法对ArrayList进行扩容:1.5倍
private void grow(int minCapacity) {
int oldCapacity = elementData.length;  //旧容量
int newCapacity = oldCapacity + (oldCapacity >> 1);  //原来的长度加原来的长度的一半(按1.5倍增长扩容)
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)//新容量大于数组中元素最大个数
newCapacity = hugeCapacity(minCapacity);//调用hugeCapacity函数
elementData = Arrays.copyOf(elementData, newCapacity);
//调用Arrays.copyOf将当前数组复制到扩容后的数组里
}
private static int hugeCapacity(int minCapacity) {

20000
if (minCapacity < 0)  //不合法的值
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
  • CRUD方法研究
//添加元素:add()
在添加元素之前,要判断数组是否已满,满了则要按1.5倍扩容,再添加元素
public boolean add(E e) {
ensureCapacityInternal(size + 1);   //调用ensureCapacityInternal方法
elementData[size++] = e; //添加完成后,数组元素的个数++
return true;
}
private void ensureCapacityInternal(int minCapacity) {
//调用calculateCapacity和ensureExplicitCapacity方法
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity){
//判断当前数组是否为空
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//判断当前需要的数组和默认数组的大小,调用Math.max方法,返回最大的
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;    //版本标记!用来记录对集合中元素所操作(增删改)的次数
if (minCapacity - elementData.length > 0)  //容量不够用则扩容
grow(minCapacity);
}
//删除元素:remove()
先找到元素存储位置(null和非null的对象在判断相等的时候不同)
注意:相同元素存在的情况下只需要找到从0号位置开始第一次出现的
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {  //null元素用==判断
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) { //非null则用equals判断相等
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);//调用System.arrayCopy将后面的数据往前复制一位
elementData[--size] = null; // 元素个数-1,clear to let GC do its work
}
//获取元素:get()
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
private void rangeCheck(int index) { //对获取的下标进行范围检查,
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
//修改元素:set()
public E set(int index, E element) {
rangeCheck(index); //对下标进行范围检查
E oldValue = elementData(index);
elementData[index] = element; //修改
return oldValue;
}

手动实现ArrayList

class Arraylist{  //自定义一个ArrayList类,两个属性
private  int[] array; //存放数据的数组
private  int index; //存储元素的位置 初始为0;

public Arraylist(){
this(5);  //默认数组长度为5
this.index = 0;
}
public Arraylist(int size){
this.array = new int[size];
}

public boolean isFull(){ //是否满
if (this.index >= array.length){ //满了,调用Arrays.copyOf进行2倍扩容
array = Arrays.copyOf(array,2*array.length);
}
return false;
}
public boolean add(int val){  //添加
if(!isFull()){ //没满可以直接添加,满了扩容后再添加
this.array[index++] = val;
return true;
}
return false;
}
public int get(int i){  //获取
if(i>=0 && i< index) {
int key = array[i];
return key;
}
throw  new IndexOutOfBoundsException("越界");

}
public boolean remove(int key){  //删除
for (int i = 0; i < array.length; i++) {
if(array[i] == key) {
array[i] = array[i + 1];
}
}
return false;
}

public void show(){  //打印
for(int i = 0;i < array.length;i++){
System.out.println(Arrays.toString(array));
return;
}
}
}
public class ArrayListDemo217 {

public static void main(String[] args) {
Arraylist arraylist = new Arraylist();
//添加
arraylist.add(1);
arraylist.add(3);
arraylist.add(5);
arraylist.add(6);
arraylist.add(8);
arraylist.add(10);
//打印
arraylist.show();

//获取
int val =  arraylist.get(2);
System.out.println(val);
//删除
arraylist.remove(3);
arraylist.show();

}
}

此次总结学会了看源码,分析源码中ArrayList的构造和编程技巧,感觉源码里,每个方法都封装的特别好,让人一目了然并且代码的可读性超级强,刚入门的菜鸟都可以看懂。以后我写代码一定要像这样学习。

ArrayList和数组的异同之处:

  1. 初始化大小:数组必须指定大小,ArrayList不需要,可以动态扩容
  2. 存储数据类型:ArrayList只能存储引用类型。数组可以存储基本类型和引用类型。
  3. ArrayList比数组灵活。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: