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。
jdk 1.7 之后 数组默认数据大小代码存放在add方法中的。
jdk1.6的时候,默认构造函数初始化elementData大小。
Arraylist底层基于数组实现
//(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);
}
}
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++;
}
//index 要插入的下标开始,
//index + 1 从要插入的下标值后面元素开始覆盖替换,
//size - index 长度;要插入下标后面元素的长度
System.arraycopy(elementData, index, elementData, index + 1, size - index);
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增加,容量增长的参数。