ArrayList 底层实现原理及Vector

ArrayList 底层实现核心点

1.集合底层使用数组实现的
2.为什么集合能存放无限大小?####数组扩容技术实现的

Arrays.copyOf:
实现数组的复制,返回复制后的数组。参数是被复制的数组和复制的长度。
返回一个新的数组,将原来数组长度2,扩容长度为10的一个新的数组,原来的数据不变。

代码示例实现:

package com.mmall.demo2.demotest;

import java.util.Arrays;

public class Test001 {
    public static void main(String[] args) {
        Object[] objects = {1, 2};
        System.out.println("原来数组长度" + objects.length);
        // 返回一个新的数组,将原来数组长度2,扩容长度为10的一个新的数组,原来的数据不变。
        Object[] objects2 = Arrays.copyOf(objects, 10);
        System.out.println("新数组的长度" + objects2.length);

    }
}

运行结果:
在这里插入图片描述

System.arraycopy方法:
如果是数组比较大,那么使用System.arraycopy会比较有优势,因为其使用的是内存复制,省去了大量的数组寻址访问等时间
参数讲解:
System.arraycopy(src,srcPos,dets,destPos, length);
src:源数组;
srcPos:源数组要复制的起始位置;–要从原数组什么位置开始复制。 下标从0开始算的。
dest:目的数组;
destPos:目的数组放置的起始位置; --从目的数组什么位置开始。 下标从0开始算的。
length:复制的长度。 --要复制数组的长度。 在使用的时候要注意别越界。

示例代码:

  int[] fun ={0,1,2,3,4,5,6};
//        src:源数组;
//        srcPos:源数组要复制的起始位置;--要从原数组什么位置开始复制。
//        dest:目的数组;
//        destPos:目的数组放置的起始位置;	--从目的数组什么位置开始。
//        length:复制的长度。 --要复制数组的长度
        System.arraycopy(fun,0,fun,3,3);
        for (int i : fun) {
            System.out.print(i);
        }

运行结果为:
在这里插入图片描述

错误示例代码:
在这里插入图片描述
复制的长度更为7:会出现数组越界报错。因为它的位置是从下标3开始,fun原数组数据,位置下标3到最后下标位置只有4个位置。所以长度最多是4。

解析JDK1.8ArrayList集合源码

jdk 1.7 之后 数组默认数据大小代码存放在add方法中的。
jdk1.6的时候,默认构造函数初始化elementData大小。
Arraylist底层基于数组实现

  1. 构造方法解析。
    默认构造方法:
//(jdk1.6的时候,默认构造函数初始化elementData大小)
 //构造方法可以知道 jdk1.7没有在构造函数中初始化大小。 
private Object[] elementData; 
//默认构造函数
public ArrayList() { 
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
      private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

有参的构造方法:


public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) { //如果参数大于2,就给数组初始化并把数组容量赋值
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else { //不能小于0 否则抛出异常。
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }
  1. add(index)解析:
    Arraylist底层默认数组初始化大小为10个object数组(jdk1.7是在add方法中进行初始化的。)
  private static final int DEFAULT_CAPACITY = 10;

在这里插入图片描述

 public boolean add(E e) {
 		//size 实际的arrayList长度。
        ensureCapacityInternal(size + 1); 
        //使用下标进行赋值
        elementData[size++] = e;
        
        return true;
    }

代码解析:
//判断是否是空数组
//比较大小,取最大值,DEFAULT_CAPACITY默认大小10,minCapacity 第一次为1

private void ensureCapacityInternal(int minCapacity) {
//判断是否是空数组
 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
 			//比较大小,取最大值,DEFAULT_CAPACITY默认大小10,minCapacity 第一次为1
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
  }
        //数组进行扩容判断。
        ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        //实际存在容量(默认为10)是否大于elementData存放的长度。
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
            
    }
    

添加元素后大于当前数组的长度,则进行扩容,将数组的长度增加原来数组的一半。 每次扩容是以1.5倍进行。
// 在原来容量的基础上进行1.5倍扩容,比如原来容量是2,2+2/2=3
int newCapacity = oldCapacity + (oldCapacity >> 1);

// 增大数组空间
	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)
	// 最少保证容量和minCapacity一样
		newCapacity = minCapacity; 
		
	if (newCapacity - MAX_ARRAY_SIZE > 0)
	 // 最多不能超过最大容量
		newCapacity = hugeCapacity(minCapacity);


	elementData = Arrays.copyOf(elementData, newCapacity);

get(index)解析:

 public E get(int index) {
 		//越界判断 index是否大于等于size(elementData.length)
        rangeCheck(index);

        return elementData(index);
    }

越界判断

 private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

根据index进行remove(index)解析:

源码:

public E remove(int index) {
		//越界检查
        rangeCheck(index);

        modCount++;
        //根据下标获取值
        E oldValue = elementData(index);
		//计算删除元素后面的长度还有多长(size=elementData.length)
        int numMoved = size - index - 1;
        
        if (numMoved > 0)
        	//后面的元素逐一进行覆盖,从删除的元素开始覆盖。
            System.arraycopy(elementData, index+1, elementData, index,numMoved);
            
         //最末尾元素为null
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }

移除arrayList值示例图
假设删除下标2,内容为c的值
在这里插入图片描述
删除之后的数组变化。
在这里插入图片描述

 //最后元素为null
 elementData[--size] = null; // clear to let GC do its work

//计算删除元素后面的长度还有多长。

//计算删除元素后面的长度还有多长
  int numMoved = size - index - 1;

//后面的元素逐一进行覆盖,从删除的元素开始覆盖。
index+1 开始复制值起始位置
index 从那开始进行替换
numMoved 要复制值的长度。

//后面的元素逐一进行覆盖,从删除的元素开始覆盖。
 System.arraycopy(elementData, index+1, elementData, index,numMoved);

根据对象 进行remove(obj)解析:
源码:

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
    }

从源码可以看出来,根据值来进行删除效率是最低的,而是如果存在相同值话,会删除第一个,第二个不会删除。

add(index,obj)解析:
源码

public void add(int index, E element) {
		//1.越界判断
        rangeCheckForAdd(index);
		//2.elementData容量初始化及容量扩容
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //
        System.arraycopy(elementData, index, elementData, index + 1, size - index);
        
        elementData[index] = element;
        
        size++;
    }

实现原理图:
过程1
在这里插入图片描述
过程2
在这里插入图片描述
过程2
在这里插入图片描述

//index 要插入的下标开始,
//index + 1 从要插入的下标值后面元素开始覆盖替换,
//size - index 长度;要插入下标后面元素的长度
 System.arraycopy(elementData, index, elementData, index + 1, size - index);

Vector底层

Vector是线程安全的,但是性能比ArrayList要低。
ArrayList,Vector主要区别为以下几点:

(1):Vector是线程安全的,源码中有很多的synchronized可以看出,而ArrayList不是。导致Vector效率无法和ArrayList相比;

 public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    }

(2):ArrayList和Vector都采用线性连续存储空间,当存储空间不足的时候,ArrayList默认增加为原来的50%,Vector默认增加为原来的2倍;

Vector 扩容源码:

  private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                         capacityIncrement : oldCapacity);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

capacityIncrement 默认为0,默认构造函数。所以Vector默认增加为原来的2倍;

(3):Vector可以设置capacityIncrement,而ArrayList不可以,从字面理解就是capacity容量,Increment增加,容量增长的参数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值