ArrayList 概述 与源码简析
1 ArrayList 创建
ArrayList<String> list = new ArrayList<>();
//构造一个初始容量为10 的空列表
- 源码分析 当我们使用上面的构造来创建ArrayList的时候,其实其实内部是创建了一个 Object[] 空的数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
2 ArrayList 添加数据
添加单个数据
//添加数据
arrayList.add("afd");
arrayList.add("afd");
arrayList.add("afd");
System.out.println(arrayList);//[afd, afd, afd]
/**
* 向ArrayList中的0号位置上 添数据
* 通过这个方法添加数据,位置限定在 0 到 arraylist.size()中
*/
arrayList.add(0,"qq");
System.out.println(arrayList);//[qq, afd, afd, afd]
- arrayList.add(0,”qq”);
- 源码中 首先调用了 rangeCheckForAdd(index);方法来检察 传入的角标的范围,不在范围内就抛出异常
- 而最终是通过 Arrays.copyOf(elementData, newCapacity)这个方法将原来集合中的内容copy到新的集合中去
//add方法的源码
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 rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
添加集合中的数据
ArrayList<Integer> arrayList2 = new ArrayList<Integer>();
arrayList2.add(1);
ArrayList<Integer> arrayList3 = new ArrayList<Integer>();
arrayList3.add(3);
//将 arraylist2 中的数据全部添加到arraylist3集合中
arrayList3.addAll(arrayList2);
System.out.println(arrayList3);//[3, 1]
- 分析其源码我们会发现,其先是通过 toArray()方法将集合数据转为数组数据, 最终是通过 System.arraycopy()方法 数据copy到了一个新的数组中
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
3 ArrayList 移除集合中的所有的数据
ArrayList<String> arrayList = new ArrayList<String>();
//添加数据
arrayList.add("afd");
arrayList.add("afd");
arrayList.add("afd");
System.out.println(arrayList);//[afd, afd, afd]
//移除集合中的所有的数据
arrayList.clear();
System.out.println(arrayList);//[]
- 分析其源码我们会发现, 其实是将 集合底层保存数据的数组中的元素设置为了 null , 同时将记录元素个数的变量size的值置为0
//clear功能源码
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
4 ArrayList 判断是否包含指定的元素
4.1 判断操作
ArrayList<String> arrayList = new ArrayList<String>();
//添加数据
arrayList.add("afd");
arrayList.add("afd");
arrayList.add("afd");
//判断 集合中是否包含指定的元素 ,饱含返回true ,不包含返回false
boolean contains = arrayList.contains("afd");
System.out.println(contains);//true
4.2 源码分析
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
可以发现其内部是调用了indexOf()方法,该方法去数组中查询指定的元素在其中第一次出现的位置 ,如果没有就返回-1,如果有就返回该位置
5 ArrayList 判断列表中是否有元素(是否不为空)
//集合为空 返回true 不为空返回false
boolean empty = arrayList.isEmpty();
源码分析
//这里的size 是集合对应的保存数据的内容数组的大小
public boolean isEmpty() {
return size == 0;
}
6 ArrayList 移除元素
ArrayList<String> arrayList = new ArrayList<String>();
//添加数据
arrayList.add("afd");
arrayList.add("abc");
arrayList.add("axc");
//方式一 移除0角标的数据
arrayList.remove(0);
System.out.println(arrayList);//[abc, axc]
//方式二 移除数据 "axc"
arrayList.remove("axc");
System.out.println(arrayList);//[abc]
源码分析
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;
}
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;
}
这是两个重载的方法 remove(int index) 是根据传入的角标,根据角标去除对应的数据,最后还是通过System.arraycopy方法来重建新的数组
remove(Object o) 是直接移除元素的方法,元素有可能为 null,所以这里有了一个判断 ,最终都是通过 fastRemove(index);方法中的System.arraycopy逻辑来创建新的数组的
7 ArrayList 修改数据
ArrayList<Integer> arrayList = new ArrayList<Integer>();
//添加数据
for (int i = 0; i <14; i++) {
arrayList.add(i);
}
System.out.println(arrayList);//[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
//修改数据 修改0 号位置上的数据为100
Integer set = arrayList.set(0, 100);
System.out.println(arrayList);//[100, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
源码分析
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));
}
- 先通过 rangeCheck(index) 方法来检验修改数据传入的角标值是否有效
- 然后通过elementData(index) 拿到数组中对应角标的数据,并返回去
- 最后重新对数组中对应角标下进行赋值
8 ArrayList 获取集合中元素的个数
ArrayList<Integer> arrayList = new ArrayList<Integer>();
//添加数据
for (int i = 0; i <14; i++) {
arrayList.add(i);
}
System.out.println(arrayList);//[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
//获取集合中元素的个数
int size = arrayList.size();
System.out.println(size);//14
源码分析
public int size() {
return size;
}
源码中 直接将记录 元素个数变化的变量size的值返回去
9 ArrayList 转为对应的数组
ArrayList<Integer> arrayList = new ArrayList<Integer>();
//添加数据
for (int i = 0; i <14; i++) {
arrayList.add(i);
}
System.out.println(arrayList);//[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
//操作方式一
Object[] objArray= arrayList.toArray();
for (int i = 0; i < objArray.length; i++) {
//取出数据
Integer num = (Integer) objArray[i];
System.out.println(num);
}
//操作方式二
//创建将要保存数据的 数组
Integer[] array = new Integer[arrayList.size()];
//将集合中的数据转存到数组中
arrayList.toArray(array);
- 操作方式一中 ,通过toArray()方法是一个Object对象数组,可以在每次取出数据的时候,将数组类型强转
源码分析
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
- toArray() 方法是直接使用了 Arrays.copyOf(elementData, size)方法
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
- toArray(T[] a)方法中 当目标数组长度小于集合元素个数的时候,通过 Arrays.copyOf () 来构建赋值数组,否则的话就会通过 System.arraycopy()方法拷贝数组