这篇文章来自个人博客数组
上一篇文章分析了ArrayList的构造器和基本操做的源码,这一次来说一讲ArrayList经常使用方法剩下的源码(迭代器的留到下次说)bash
主要内容ui
- 容器容量
- 查找特定元素,包含特定元素,克隆之类的基本用法
开始正题以前,须要先分清楚两个概念,容器的capacity和size,capacity指的是容器的容量,最多能装多少,而size指的是当前容器中元素的数量this
接下来直奔正题spa
public void ensureCapacity(int minCapacity) {
//若是初始时有设定容量,就按设定的来,没有就按默认的来
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
// any size if not default element table
// larger than default for default empty table. It is already
// supposed to be at default size.
//: DEFAULT_CAPACITY;
//若是给定的比上述的要大,就按给定的来
if (minCapacity > minExpand) {
//这个方法在下面
ensureExplicitCapacity(minCapacity);
}
}
//私有方法,用来肯定容量
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
//增长容量
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
复制代码
/**
* The maximum size of array to allocate.
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
*/
//注解说是由于有的虚拟机要保留数组的前几位,这个常量的值为2147483639
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
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);
//按照新容量复制数组
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
复制代码
private static int hugeCapacity(int minCapacity) {
//溢出
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
复制代码
若是发现容量多了不少,有点浪费,就能够调整容器大小到当前大小3d
public void trimToSize() {
modCount++;
//若是确实有多余部分
if (size < elementData.length) {
//若是size不为0,则按照size复制数组
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
复制代码
public int size() {
return size;
}
复制代码
public boolean isEmpty() {
return size == 0;
}
复制代码
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
//遍历数组查找
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
复制代码
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
复制代码
public int lastIndexOf(Object o) {
if (o == null) {
for (int i = size-1; i >= 0; i--)
if (elementData[i]==null)
return i;
} else {
//从尾部开始遍历
for (int i = size-1; i >= 0; i--)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
复制代码
须要先说明一个概念:浅拷贝和深拷贝指针
浅拷贝,两个ArrayList在内存中的地址是不同的,可是他们中的元素都指向同一个地址,手绘一个图解释一下:code
深拷贝,不只ArrayList在内存中的地址是不同的,他们中的元素也不指向同一个地址,也手绘个图解释一下:cdn
深拷贝可能好理解一些,就是全部的东西都拷贝一下,彻底分离的状态blog
ArrayList的**clone()**方法是浅拷贝,咱们来作个实验:
public class Test {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(String.valueOf(i));
}
//强制类型转换
ArrayList<String> list1 = (ArrayList<String>)list.clone();
System.out.println("list" + list);
System.out.println("list1" + list1);
System.out.println("两者对于某个特定元素是否相同 " + (list.get(1) == list1.get(1)));
System.out.println("两者的地址是否相同 " + (list == list1));
}
}
复制代码
ArrayList是一个列表,和数组不同,不能直接用下标访问元素,转换为数组就能够了,转换为数组有两种方法:
//按照列表中元素的顺序,将列表中元素复制到一个数组
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
复制代码
这一种方法可能会抛出数组存储异常和空指针异常,这里讲述两种状况(数组大小小于列表元素数量):
传入的数组类型和列表元素相同或者是列表元素的基类型,直接将列表中元素赋值在数组内
传入的数组类型不是列表元素的基类型,抛出数组存储异常
public <T> T[] toArray(T[] a) {
//传入的数组大小更小
if (a.length < size)
//建立一个数组,类型是数组a的运行时类型
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
复制代码
传入的数组 s 是Object类型的,符合上述的状况一:
接下来是状况二:
这样子,ArrayList经常使用的方法就讲完了,下一篇就会是与ArrayList有关的迭代器的使用了