【Java基础】Java8集合[ArrayList 常用方法讲解](源码分析+底层原理)
CSDN话题挑战赛第2期
参赛话题:学习笔记
Java8集合:ArrayList
- 一、前言
- 二、集合ArrayList方法
- 1.构造方法
- ①空参构造器
- ②带参构造器
- 2.grow方法
- 3.size 方法
- 4.isEmpty方法
- 5.add方法
- 6.get方法
- 7.set方法
- 8.indexOf方法
- 9.lastIndexOf方法
- 10.remove方法
一、前言
集合主要分为Collection接口
与 Map接口
两类,而ArrayList
就是Collection
接口中List
子接口的实现类…
在上一篇文章中,我们通过ArrayList实现类的源码,了解到了其中的属性,比较印象深刻的应该是ArrayList集合底层由一个Object[]
数组实现,且可以存放包括null
在内的所有元素。
而在这篇文章中,我们将讲解集合中ArrayList
实现类的方法
:
二、集合ArrayList方法
1.构造方法
①空参构造器
源码
:
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
这是源码中的空参构造器,当我们创建ArrayList对象,没有设置参数传递,也就是默认情况下,存储数据的数组elementData
是一个长度为0的空数组。
List list = new ArrayList();
②带参构造器
源码
:
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);
}
}
当我们创建ArrayList对象,且设置了传入参数,即指定了初始大小的时候,存储数据的数组elementData
初始大小就变成了我们指定的初始大小了。
List list = new ArrayList(0);
2.grow方法
源码
:
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
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);
}
集合的扩容机制:
ArrayList集合的底层数组,在扩容的时候会用到grow方法,我们从源码中看到newCapacity = oldCapacity + (oldCapacity >> 1);
相当于:新的容量 = 旧的容量 + 旧的容量 / 2;
所以ArrayList
每次扩容都是扩容为1.5
倍,然后调用Arrays类中的copyOf方法,将元素拷贝到一个新的数组当中…
3.size 方法
源码
:
public int size() {
return size;
}
代码很简单,就是返回size的值,在上一篇文章中,我们已经熟悉过size属性:
The size of the ArrayList (the number of elements it contains).
返回的size值代表的是集合中存在的元素个数,注意:返回的不是数组的实际空间。
int count = list.size();
4.isEmpty方法
源码
:
public boolean isEmpty() {
return size == 0;
}
此方法用于判断集合是否为空,方法中使用了上文提及的size属性:
- 当size == 0 时,代表集合中没有元素,返回true
- 当size != 0 时,代表集合中存在元素,返回false
boolean a = list.isEmpty();
5.add方法
源码
:
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
使用add方法时:
再插入元素之前,会检查先检查是否需要扩容,之后将传入的元素存放在数组最后一个元素后面的位置。
源码中调用了ensureCapacityInternal();
方法:
当存储数据的数组elementData为空数组时,就使用默认数组长度去扩容,也就是长度默认为0,只有真正有元素传入,才会使用grow()方法创建长度为10的数组。
element放在最后元素的就没:
list.add(element);
element放在指定下标index的位置上,若位置存在元素,该元素与后续元素向后移动一位,element依旧会放置在指定的index位置上:
list.add(index,element);
6.get方法
源码
:
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
E elementData(int index) {
return (E) elementData[index];
}
将get方法中用到的方法也放进来了,结构不算复杂:
- 先是使用
rangeCheck(index)
方法判断是否越界; - 之后就是通过传入的
index
下标获取对应元素;
list.get(index);
7.set方法
源码
:
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
set方法与上文提到的get方法类似:
- 先是使用
rangeCheck(index)
方法判断下标index
是否越界; - 然后就是将
index
下标对应的元素替换成传入的element
;
list.set(index,element);
8.indexOf方法
源码
:
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;
}
在indexOf方法中,我们传入一个元素,方法会返回集合中第一个等于传入元素的元素下标。
方法中运用了循环遍历集合中每个元素来查找是否存在与传入元素相同的元素…
我们还可以注意到,传入的元素包含null,再一次说明了集合可以存储包含null在内的全部元素。
int first_index = list.indexOf(null);
9.lastIndexOf方法
源码
:
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;
}
lastIndexOf方法与上文提到的indexOf方法原理基本一致,但是遍历是从后往前
的,也就是说返回的是最后一个
与传入元素相同的元素下标
。
int last_index = list.lastIndexOf(null);
10.remove方法
源码
:
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
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
return oldValue;
}
移除此列表中指定位置index上对应的元素。将所有后续元素向左移动一位(从它们的索引中减去1)。
list.remove(index);
到这里,集合ArrayList中较为常用的方法也就讲解完了,同时借助源码,我们还讲解了一些集合的底层实现原理,增加了对集合的认识,很棒,对吗~