【JDK源码】ArrayList

一、ArrayList概述

数组:一旦初始化长度就不可以发生变化。

  • 增删慢:每次增删元素,都需要改变数组的长度,且移动元素的位置。
  • 查询快:数组在内存中是一块连续的空间,可根据地址+索引的方式快速获取对应位置上的元素。

ArrayList:List接口的大小可变数组的实现。

ArrayList底层的数据结构就是数组,数组元素类型为Object类型。对ArrayList类的实例的所有的操作,在底层都是基于数组的。

二、 ArrayList继承关系

在这里插入图片描述

2.1 Serializable标记性接口

2.1.1 Serializable介绍

类的序列化由实现java.io.Serializable接口的类启用。 不实现此接口的类将不会使任何状态序列化或反序列化。 可序列化类的所有子类型都是可序列化的。 序列化接口没有方法或字段,仅用于标识可串行化的语义。

  • 序列化:将对象的数据写入到文件(写对象)。
  • 反序列化:将文件中对象的数据读取出来(都对象)。

2.1.2 Serializable源码

public interface Serializable {
	// 该接口仅用于标识,没有方法和字段
}

2.2 Cloneable标记性接口

2.2.1 Cloneable介绍

一个类实现Cloneable接口,以指示Object.clone()方法,该方法对于该类的实例进行现场复制是合法的。

在不实现Cloneable接口的实例上调用对象的克隆方法导致抛出异常CloneNotSupportedException 。

2.2.2 Cloneable源码

public interface Cloneable {
	// 该接口没有字段和方法名
}

2.2.3 克隆的前提条件

  • 被克隆对象所在的类必须实现Cloneable接口。
  • 必须重写clone()方法。

2.2.4 ArrayList中的clone()方法源码

public Object clone() {
    try {
    	// 调用Object中的clone()
        ArrayList<?> v = (ArrayList<?>) super.clone(); 
        
        // 将ArrayList对象中的元素,拷贝成长度相同的Object[] elementData数组。
        v.elementData = Arrays.copyOf(elementData, size);
        
        v.modCount = 0;
        return v;
    } catch (CloneNotSupportedException e) {
        // this shouldn't happen, since we are Cloneable
        throw new InternalError(e);
    }
}

2.2.5 浅克隆与深克隆

  1. 浅克隆

    直接调用父类Object类的clone()方法。

    局限性:基本数据类型可以达到完全复制。但是引用数据类型不可以,它们会相互影响。

    原因:引用数据类型仅仅克隆了一份引用(即地址),当它的值发生改变时,被克隆对象的属性值也会发生改变。

  2. 深克隆

    不能直接调用父类Object类的clone()方法,需要程序员手动重写。既要克隆该对象,也要更深一步依次克隆对象的所有引用数据类型的属性,再将属性重写赋值。

    深克隆出对象中的所用数据,与原来被克隆对象中的数据不会相互影响,彻底分离。

@Override
public Object clone() throws CloneNotSupportedException {
	// 浅克隆
	//return super.clone(); 

	// 深克隆
	Student stu = (Student) super.clone(); // 将对象克隆
	Skill skill = (Skill)this.skill.clone(); // 将对象的引用数据类型的属性克隆
	stu.setSkill(skill); // 将属性重新赋值
	return stu;
}

2.3 RandomAccess标记性接口

2.3.1 RandomAccess介绍

List实现使用的标记界面,表明它们支持快速(通常为恒定时间)随机访问。 此接口的主要目的是允许通用算法更改其行为,以便在应用于随机访问列表顺序访问列表时提供良好的性能。

如果一个List实现了这个接口,则随机访问比顺序访问更快。

随机访问列表

for (int i=0, n=list.size(); i < n; i++)
	list.get(i); 

顺序访问列表

for (Iterator i=list.iterator(); i.hasNext(); )
	i.next(); 

2.3.2 RandomAccess源码

public interface RandomAccess {
	// 该接口没有字段和方法名
}

2.4 AbstractList抽象类

该类提供了List接口的骨架实现。

三、ArrayList源码分析

3.1 类的属性

// 序列化ID
private static final long serialVersionUID = 8683452581122892189L;

// 默认初始化容量为10
private static final int DEFAULT_CAPACITY = 10;

// 容量大小为0的空实例的共享数组/
 private static final Object[] EMPTY_ELEMENTDATA = {};

// 默认容量大小的空实例的共享数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

/**
 * 存储ArrayList元素的数组缓冲区
 * 当elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA,且
 * 在添加第一个元素时,扩展为默认容量
 */
transient Object[] elementData; 

// ArrayList的实际大小(数组包含的元素个数/实际数据的数量)默认为0
private int size;

3.2 构造器

3.2.1 ArrayList(int initialCapacity)

/**
 * 构造具有指定初始容量的空列表
 *
 * @param  initialCapacity  列表的初始容量
 * @throws IllegalArgumentException 指定容量大小不合法,抛出非法数据异常
 */
public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
    	// 指定容量
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
    	// 容量大小为0
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
    	// 指定容量不合法,抛出异常
        throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
    }
}

3.2.2 ArrayList()

/**
 * 构造一个初始容量为10的空列表
 */
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

3.2.3 ArrayList(Collection c)

/**
 * 构造一个包含指定 collection 的元素的列表,
 * 这些元素是按照该 collection 的迭代器返回它们的顺序排列的
 * 
 * @param c 要将其元素放入此列表的集合
 * @throws NullPointerException 集合为空,抛出空指针异常
 */
public ArrayList(Collection<? extends E> c) {
    Object[] a = c.toArray(); // 将集合c装成数组
    if ((size = a.length) != 0) {
        if (c.getClass() == ArrayList.class) {
            elementData = a;
        } else {
        	// 数组的创建和拷贝
            elementData = Arrays.copyOf(a, size, Object[].class);
        }
    } else {
        // 集合c为空
        elementData = EMPTY_ELEMENTDATA;
    }
}

3.3 主要方法

3.3.1 get(int index)

/**
 * 返回此列表中指定位置的元素。
 *
 * @param  index 要返回元素的索引
 * @return 指定位置的元素
 * @throws IndexOutOfBoundsException 索引越界异常
 */
public E get(int index) {
	// 检查索引范围
    rangeCheck(index);
    // 返回指定位置元素
    return elementData(index);
}


/**
 * 检查指定索引是否在范围内。
 */
private void rangeCheck(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

/**
 * 返回指定位置元素
 */
@SuppressWarnings("unchecked")
E elementData(int index) {
    return (E) elementData[index];
}

3.3.2 set(int index, E element)

/**
 * 替换指定位置的元素
 *
 * @param index 要替换元素的索引
 * @param element 要存储在指定位置的元素值
 * @return 指定位置原来的元素
 * @throws IndexOutOfBoundsException {@inheritDoc}
 */
public E set(int index, E element) {
    rangeCheck(index); // 越界检查
    E oldValue = elementData(index);
    elementData[index] = element;
    return oldValue;
}

3.3.3 grow(int minCapacity)

/**
* 扩容
*
* @param minCapacity 所需的最小容量
*/
private void grow(int minCapacity) {
	// overflow-conscious code
	// 获取当前数组的容量
	int oldCapacity = elementData.length;
	// 扩容。新容量 = 旧容量 + 旧容量/2
	int newCapacity = oldCapacity + (oldCapacity >> 1);

	// 扩容后的新容量 小于 所需最小容量
	if (newCapacity - minCapacity < 0)
	// 将容量再次扩容成所需最小容量
	newCapacity = minCapacity;
	
	// 扩容后的新容量 大于 最大容量临界值
	if (newCapacity - MAX_ARRAY_SIZE > 0)
	// 再次扩容
	newCapacity = hugeCapacity(minCapacity);
	// 拷贝数组
	elementData = Arrays.copyOf(elementData, newCapacity);
}

3.3.4 add(E e)

/**
* 在列表末尾添加元素
*
* @param e 要添加的元素
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
public boolean add(E e) {
	// 确保容量,容量不够需扩容
	ensureCapacityInternal(size + 1);  // Increments modCount!!
	elementData[size++] = e;
	return true;
}
	
private void ensureCapacityInternal(int minCapacity) {
	ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
	
// 确保容量。容量不够就扩容。
private void ensureExplicitCapacity(int minCapacity) {
	modCount++; // 操作次数+1
	
	// 所需最小容量 大于 数组长度
	if (minCapacity - elementData.length > 0)
	grow(minCapacity); // 扩容
}

3.3.5 add(int index, E element)

    /**
     * 在指定位置插入元素
     *
     * @param index 要插入元素的位置索引
     * @param element 要插入的元素
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public void add(int index, E element) {
    	// 	越界检查
        rangeCheckForAdd(index);
		// 确保容量
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        
        // 拷贝数组,空出index位置,后面元素后移一位
        // arraycopy(源数组,源数组中元素移动的起始位置,目标数组,目标数据中的起始位置,要复制的数组元素的数量)
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

3.3.6 remove(Object o)

 /**
  * 移除指定元素的第一个匹配元素
  *
  * @param o 需要删除的元素
  * @return <tt>true</tt> if this list contained the specified element
  */
 public boolean remove(Object o) {
     if (o == 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; // clear to let GC do its work
 }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值