今天,让我们一起走进ArrayList类,了解ArrayList类背后的故事
ArrayList类:
-
逻辑结构:首先我们要知道ArrayList类是实现了List接口,而
List接口又继承了Collection接口。正是由于接口和继承的使用,使ArrayList类具有强大的能力和丰富的功能。 -
特点:①有序性 ②允许重复
-
存储结构:内部利用了Object[]数组进行对元素的存储;
-
用处:ArrayList类对集合可以进行一系列操作(类似于Array类对数组进行一系列操作),并且由于存储结构的特点,相当于集合为加强版的数组(提供了比数组更多的操作)。
集合和数组的区别
1.集合比数组更为方便,并且提供了更多的操作。
2.数组长度固定,集合可以对长度进行扩容
3.数组运行速率较快,集合运行速率较慢(集合在数组外加了一系列逻辑相关的操作)
初始化(引用+创建)
1.非泛型:
ArrayList list=new ArrayList()
list.add("小明");
list.add(23) ; //可以添加任何类型值
可以创建任何Object子类元素集合。
2.泛型:
ArrayList<String> list01=new ArrayList<String>();
list01.add("小飞");
//list01.add(23) ; //不能为除String类型之外的任何类型
利用泛型创建集合对象,则类型为指定的泛型,不能使用其他类型。
注:当为无参构造时,传入的值为默认空数据,则不创建数组,只有进行一次添加元素时,才使传入值大于默认空数据,从而创建数组。
常见方法
-
add()
用处:向集合中添加指定元素
参数:指定泛型的值
返回:boolean类型的值
注:默认添加的值都放于集合最后面 -
addAll()
用处:将一个集合的所有元素全部添加到另一个集合
参数:Collection下的子类型
返回:boolena类型的值 -
get()
用处:根据指定下标,得到该下标所对应的集合元素
参数:int类型的index下标
返回:集合中的元素 -
size()
用处:返回当前集合中元素的个数
参数:无
返回:int类型的值 -
contains()
用处:判断元素是否在该集合中
参数:集合中的值
返回:boolean类型的值,有返回true,否则返回false -
indexOf()
用处:查找集合内指定元素的位置
参数:集合中的指定元素
返回:存在返回当前指定元素下标,不存在返回-1 -
remove()
用处:
1.移除指定下标位置所对应的元素,并返回未移除之前的值
2.移除指定元素
参数:
1.int类型指定下标位置
2.指定元素
返回:
1.未移除之前的值
2.成功移除:true ;否则:false
8.set()
用处:将指定下标位置的值修改为指定元素
参数:指定下标,指定元素
返回:未修改的原值
9.sort()
用处:给集合排序
参数:Comparator比较器接口实现类对象
返回:
10.to Array()
用处:将集合转换为数组
参数:无参
返回:Object[]数组
ArrayList类的扩容机制
注:要记着去抽一下源码,才能最好的去理解
抓住:grow(minCapacity)
问题1:若以无参构造创建对象,则内部Object[]数组是否创建?数组长度为多少?若是超出指定长度,则如何扩容?
问题2:若以有参构造创建对象,则内部Object[]数组长度不足以使用,则我该如何扩容,若是超出最大长度,我们该怎么办?
核心代码:
int newCapacity = oldCapacity + (oldCapacity >> 1);
//对旧容量增加到1.5倍(增加了0.5倍),存入newCapacity(新容量)
若为无参构造:
①确保数组长度够用,则使最小容量为size(数组中元素个数)+1(minCapacity);
②由于无参构造,则数组中为空值,和系统默认空数据相等,则系统扩容到10(此时数组长度为10),返回minCapacity。(该判断在无参构造中只进行第一次)
下次判段,直接返回minCapacity;
有参无参构造的公共部分
③如果minCapacity<此时数组长度(element.length),则不需扩容
若是minCapacity>数组长度,则扩容 grow(minCapacity);
④ grow(minCapacity)
令minCapacity先暂存 与oldCapacity(旧容量)
int newCapacity = oldCapacity + (oldCapacity >> 1);
//对旧容量增加到1.5倍(增加了0.5倍),存入newCapacity(新容量)
if (newCapacity - minCapacity < 0)
//若新容量还是小于要存入的元素个数,则令新容量为元素个数值
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
//若新容量大于int的最大值-8,再进行扩容处理
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
//利用数组复制,得到新的数组长度
⑤
若是新的容量>Integer.MAX_VALUE-8
判断继续扩容
若元素个数大于Integer.MAX_VALUE(大于int范围则为负数),则抛出错误
若元素个数大于Integer.MAX_VALUE-8,小于Integer.MAX_VALUE, 则返回Integer.MAX_VALUE,否则返回Integer.MAX_VALUE-8
;
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0)
//若元素个数大于int的最大值(大于int范围则为负数),则抛出错误
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
//若元素个数大于int的最大值-8,小于int最大值, 则返回int最大值,否则返回int最大值-8
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
}
阉割后的源代码,以及注释,希望对大家有用
public class ArrayList
{
// 构造方法
public ArrayList() {
//由于为无参构造,则该元素为默认空数据
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
public ArrayList(int initialCapacity) { //有参构造
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity]; //传入的长度大于0,则创建Object类的数组,长度为传入的值
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA; //传入长度为0,则不创建数组,使元素为默认空数据
} else {
throw new IllegalArgumentException("Illegal Capacity: "+ //当传入长度小于0,则抛出参数不合法异常
initialCapacity);
}
}
// 基础扩容方法
// 添加元素
public boolean add(E e) {
// 按照元素个数+1,确认数组容量是否够用
ensureCapacityInternal(size + 1);
//当扩容完毕,将新元素添加到数组中,并使下标自加
elementData[size++] = e;
return true;
}
//判断是否需要
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
//计算容量
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { //当为无参构造时,判断元素内容与默认空数据相等
return Math.max(DEFAULT_CAPACITY, minCapacity); //返回一个minCapacity=1,与DEFAULT_CAPACITY=10,选择一个最大值 10;
}
return minCapacity; //无参构造,第一次返回1,有参构造,返回传入的参数值+1
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0) //如果传入的元素个数+1 > 数组长度
grow(minCapacity); //对数组进行扩容
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length; //令数组长度先暂存与oldCapacity(旧容量)
int newCapacity = oldCapacity + (oldCapacity >> 1); //对旧容量增加到1.5倍(增加了0.5倍),存入newCapacity(新容量)
if (newCapacity - minCapacity < 0) //若新容量还是小于要存入的元素个数,则令新容量为元素个数值
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0) //若新容量大于int的最大值-8,再进行扩容处理
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity); //利用数组复制,得到新的数组长度
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow //若元素个数大于int的最大值,则抛出错误
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ? //若元素个数大于int的最大值-8,小于int最大值, 则返回int最大值,否则返回int最大值-8
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
}
今天的内容就到这里,扩容机制,一定要结合源码进行分析,可以结合我的说的问题,去思考,学习。要是有什么错误的地方,请大家及时指正,谢谢!
让我们一起加油学习,做好自己,奥力给!!!