解析字节算法面试真题,深入探究 ArrayList 应用原理
ArrayList是Java的链表类,作为Java三大容器组成之一的List的组成部分,如下图所示
在项目开发中常用,虽然他有一些不足的地方,但是这不足以替代他帮助程序员解决大量问题的光辉,正是由于他被经常使用,所以在面试的时候也被经常问到,比方说字节、腾讯这一类对于算法实现以及源码考察比较重视的公司更是如此,今天我就通过几道面试题,以面试的身份对于ArrayList进行讲解
关注公众号:Java架构师联盟,每日更新技术好文
题目:【java源码】ArrayList
ArrayList 常用功能:构造函数、增、批量增、删、批量删、批量保留
ArrayList 属性:
// 默认数组长度(数组,而不是数据个数)
private static final int DEFAULT_CAPACITY = 10;
// 空数据
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 实际用于存放数据的地方
transient Object[] elementData;
// 数据个数
private int size;
ArrayList 常用功能:构造函数、增、批量增、删、批量删、批量保留ArrayList 属性:
// 默认数组长度(数组,而不是数据个数)
private static final int DEFAULT_CAPACITY = 10;
// 空数据
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 实际用于存放数据的地方
transient Object[] elementData;
// 数据个数
private int size;
1、构造函数
①public ArrayList();
只干了一件事:this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
②public ArrayList(int initialCapacity);
指定初始化elementData数组的初始大小。this.elementData = new Object[initialCapacity];
③public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
2、增:就是数组中插一个元素操作思路
①public boolean add(E e);
1、确保elementData数组能够装下
首先判断原来数组长度是否为0,如果为零,那么新数组长度为Math.max(DEFAULT_CAPACITY, 加入后数组最小应该长度);
判断原来数组是否还装的下,如果需要扩容那么:
private void grow(int minCapacity) { // 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);
}
2、elementData[size++] = e;return true;
②public void add(int index, E element);
1、rangeCheckForAdd(index); // 检测index是否越接 if (index > size || index < 0)
2、确保elementData数组能够装下
3、System.arraycopy(elementData, index, elementData, index + 1, size - index); // 数组挪位
4、elementData[index] = element;
5、size++;
3、批量增:就是数组中插多个元素操作思路
①public boolean addAll(Collection<? extends E> c);
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // 同上:确保elementData数组能够装下
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
②public boolean addAll(int index, Collection<? extends E> c)
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index);
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // // 同上:确保elementData数组能够装下
int numMoved = size - index;
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
4、删:三点注意①按内容删,只删除第一个;②删的是equals为真的;③注意看fastRemove(index)源码
①public boolean remove(Object o);
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;
}
②public E remove(int index); // 同理
③private void fastRemove(int index);
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
}
5、批量删、批量保留:一点注意:batchRemove方法。
①public boolean removeAll(Collection<?> c);
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, false);
}
②public boolean retainAll(Collection<?> c);
public boolean retainAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, true);
}
③private boolean batchRemove(Collection<?> c, boolean complement);
// 思想:维护两个指针。一个读指针(r),一个写指针(w)。读指针从0遍历到数组尾,在遍历中,如果符合条件就elementData[w++] = elementData[r];
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;
int r = 0, w = 0;
boolean modified = false;
try {
for (; r < size; r++)
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
if (w != size) {
// clear to let GC do its work
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}
总结:
1、ArrayList通过内置一个Object数组实现顺序表功能。通过grow函数实现动态增长,最大长度可在源码中找答案。2、删除功能通过fastRemove函数实现对一个删除,通过batchRemove函数实现对多个删除。通过elementData[i] = null;或elementData[--size] = null; 的方式,让JVM垃圾回收,自动回收。
private void grow(int minCapacity) { // 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);
}
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
}
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;
int r = 0, w = 0;
boolean modified = false;
try {
for (; r < size; r++)
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
if (w != size) {
// clear to let GC do its work
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}
版权声明: 本文为 InfoQ 作者【小Q】的原创文章。
原文链接:【http://xie.infoq.cn/article/14b02a6e2db08577f5f155acf】。
本文遵守【CC BY-NC-ND】协议,转载请保留原文出处及本版权声明。
小Q
还未添加个人签名 2020.06.30 加入
小Q 公众号:Java架构师联盟 作者多年从事一线互联网Java开发的学习历程技术汇总,旨在为大家提供一个清晰详细的学习教程,侧重点更倾向编写Java核心内容。如果能为您提供帮助,请给予支持(关注、点赞、分享)!
评论