JDK源码学习02-ArrayList源码和简单实现

ArrayList源码学习

简介

简单实现见文章底部

ArrayList是常用的集合,ArrayList查询效率高,增删效率低
自己实现ArrayList最需要注意的细节:

  1. 加法优先级高于位运算,故必须要加括号才行
	int oneAndHalf = elementData.length + (elementData.length >> 1);
  1. 删除元素,往前移动index后的元素,移动之后,最后一个元素要置位null,否则会产生内存泄漏,比较容易产生内存泄漏的还有threadlocal
 public E remove(int index) {
        checkIndex(index);
        E t=(E)elementData[index];
        System.arraycopy(elementData,index+1,elementData,index,size-index-1);
        //设置为空 以免产生内存溢出
        elementData[size-1]=null;
        size--;
        return t;
    }
  1. 查询元素和判断元素相等的时候,需要把null值额外判断一遍
    remove(Object o)
    indexOf(Object o)

复制数组的重要工具类

# 最常用最重要   特别是原数组与目标数组为同一个数组的时候
System.arraycopy(原数组, 原数组开始索引, 目标数组,目标数组开始索引,
                       复制长度);

# 拷贝原数组前newLength个元素生成一个新数组 
public static T[] Arrays.copyOf(T[] original, int newLength)

# 拷贝original的索引为[form,to)返回一个新数组
public static <T> T[] copyOfRange(T[] original,
                                  int from,
                                  int to)

静态成员

没有特别意义的两个常量: 但是由于引用的地址不同,可以区别懒加载还是初始化容量0

  1. EMPTY_ELEMENTDATA 在new ArrayList(int initialCapacity) 指定长度为0时使用,
    在其他任意表示elementData 为空时也会使用
  2. DEFAULTCAPACITY_EMPTY_ELEMENTDATA仅在new ArrayList() 没有指定长度时才会使用
# 默认初始容量  但是会使用懒加载
private static final int DEFAULT_CAPACITY = 10;

#  new ArrayList(int initialCapacity)   指定长度为0时
private static final Object[] EMPTY_ELEMENTDATA = {};

# new ArrayList()    没有指定长度时
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

# 最大长度(比较重要)
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

普通成员

注意不是使用泛型数组 而是使用 Object[]数组

# 存储数据的数组
transient Object[] elementData; // non-private to simplify nested class access

# 记录当前存储元素个数
private int size;

构造函数

无参构造
- 默认初始化DEFAULTCAPACITY_EMPTY_ELEMENTDATA={}

    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

有参构造
- 如果指定了长度 就立即初始化为当前长度(当要添加大数据量的时候,可以避免重复扩容)
- 如果指定长度为0 则为EMPTY_ELEMENTDATA={}

    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

grow()扩容函数

只得注意的是,位运算的优先级低于加减法,所以需要括号括起来,否则会变成(oldCapacity+oldCapacity)/2,失去了扩容的效果

    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        //扩容到1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //如果不能扩容到1.5倍  则扩容到当前期望的minCapacity
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;

        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

如何保证增删改查

添加元素
  1. 末尾插入
    检查当前数组长度 确保内部容量 如果不足size+1 则grow扩容
    public boolean add(E e) {
    	// 1. 检查当前数组长度  确保内部容量   如果不足size+1 则grow扩容
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
	// 3. 懒加载的实现部分  如果new ArrayList()就会在这里实现懒加载
	private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
	// 2. calculateCapacity会返回能够承载当前元素的容量(默认值10 或者 minCapacity)
    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
    // 4. 如果最小容量大于数组长度 则需要扩容
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
  1. 指定位置插入

System.arraycopy(原数组, 原数组开始索引, 目标数组,目标数组开始索引,复制长度);

这里我们可以好好研究一下为什么System.arraycopy(elementData, index, elementData, index + 1,size - index);将从index开始的元素全部往后移了一位。
例如: arr= [4,8,7,4,5,3,null,null,…] ,此时size=6
如果 我们想在index2 ,即8的后面添加元素1 只需要将数组变成 arr2= [4,8,7,7,4,5,3,null,…] ,然后将arr2[2]=2即可
怎么实现呢?
我们需要 [2,5] 的元素向后移动1格 也就是从**2(index)**开始 拷贝到从 index+1 开始的空间中
并且移动的元素有{7,4,5,3}共 4(size-index) 个元素

   public void add(int index, E element) {
      rangeCheckForAdd(index);
      //检查当前数组长度  确保内部容量   如果不足size+1 则grow扩容
      ensureCapacityInternal(size + 1);  // Increments modCount!!
      //从index开始  所有元素后移一个
      System.arraycopy(elementData, index, elementData, index + 1,
                       size - index);
      elementData[index] = element;
      size++;
  }
   public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //从index开始  所有元素后移一个
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }
删除元素

因为只用移动index后面的元素向前一格 故移动的数量是 int numMoved = size - index - 1;

    public E remove(int index) {
        rangeCheck(index);
        modCount++;
        E oldValue = elementData(index);
        int numMoved = size - index - 1;
        if (numMoved > 0)//将index后面的元素向前移动
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work
        return oldValue;
    }
    
    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
    }

ArrayList简单实现

import java.util.Arrays;

/**
 * @Author MinXu
 * @Date 2020/12/21 23:16
 * QQ   754647431
 */
public class MyList<E> {
    //Default capacity
    private static final int DEFAULT_CAPACITY = 10;

    private static final Object[] EMPTY_ELEMENTDATA = {};

    private Object[] elementData;

    private int size;

    public MyList() {
        elementData = EMPTY_ELEMENTDATA;
    }

    public MyList(int capacity) {
        if (capacity > 0)
            elementData = new Object[capacity];
        else
            elementData = EMPTY_ELEMENTDATA;
    }

    void ensureCapacity(int minCapacity) {
        if(elementData==EMPTY_ELEMENTDATA){
            elementData=new Object[DEFAULT_CAPACITY];
            return;
        }
        //需要扩容
        if (minCapacity >= elementData.length) {
            //加法优先级高于位运算
            int oneAndHalf = elementData.length + (elementData.length >> 1);
            Object[] a;
            if (minCapacity > oneAndHalf) {
                a = new Object[minCapacity];
            } else {
                a = new Object[oneAndHalf];
            }
            System.arraycopy(elementData,0,a,0,size);
            elementData=a;
        }
    }
    void checkIndex(int index){
        if(index<0)
            throw new IndexOutOfBoundsException("索引小于0");
        if(index>size-1)
            throw new IndexOutOfBoundsException("索引越界");
    }
    public void add(E e) {
        ensureCapacity(size + 1);
        elementData[size++]=e;
    }
    public E remove(int index) {
        checkIndex(index);
        E t=(E)elementData[index];
        System.arraycopy(elementData,index+1,elementData,index,size-index-1);
        //设置为空 以免产生内存溢出
        elementData[size-1]=null;
        size--;
        return t;
    }
    public void add(int index,E e) {
        checkIndex(index);
        ensureCapacity(size + 1);

        System.arraycopy(elementData,index,elementData,index+1,size-index);
        elementData[index]=e;
        size++;
    }

    public E get(int index) {
        checkIndex(index);
        return (E)elementData[index];
    }
    public int size() {
        return this.size;
    }
    public void print() {
        System.out.println(Arrays.toString(elementData));
    }
    @Override
    public String toString() {
        if(size==0)
            return "[]";
        StringBuilder res = new StringBuilder("[");
        for(int i=0;i<size-1;i++){
            res.append(elementData[i].toString()).append(", ");
        }
        res.append(elementData[size-1].toString());
        res.append("]");
        return res.toString();
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值