当向ArrayList中添加和删除元素时都需要进行元素的移动,当添加和删除的是动态数组的头部元素,需要将数组中所有元素进行移动,其最坏情况的复杂度为O(n)。
那么能不能在添加和删除元素时不进行移动或者少移动元素呢?
我们可以在动态数组的基础上增加一个int front
来记录一下首元素的位置。
在进行添加和删除,肯定需要修改front,下面先来看下ArrayList的add和remove方法。
1、ArrayList的add方法
先来看下之前动态数组的实现
public void add(int index, E element) {
checkIndexForAdd(index);
ensureCapacity(size+1);
for (int i = size - 1; i >= index; i--) {
elements[i + 1] = elements[i];
}
elements[index] = element;
size++;
}
可以看到这里是先将元素向后移动,然后将数据设置到index的位置,过程如下
image.png
如果使用了front,在进行元素添加时如何实现不移动元素或者少移动元素呢?下面分三种情况进行分析:
1.1、当向数组头部添加元素
即调用add(0,element)。
由图可知,在调用add(0,11)时,我们可以将元素添加到
index=0
的位置,然后将front减一
此时front变成0。image.png
如果再次调用add(0,55),那么此时可以向
index=5
的位置添加元素。image.png
有上面分析可知,在每次调用add(0,element)时,都会将元素添加到数组中角标=front-1的位置,即
elements[front-1]=element
。当front-1<0时,将元素添加到(front-1)+elements.length的位置。代码如下:
if (index == 0) {
front = index(-1);
elements[front] = element;
}
// 修正index
private int index(int index) {
index += front;
if (index < 0)
index += elements.length;
else
index = index % elements.length;
return index;
}
1.2、当向数组末尾添加元素
image.png此时size=4,调用add(size,66)方法,会向index=5的位置添加元素,即
elements[front+size]=66。
image.png
此时size=5,接着调用add(size,77),会向index=0的位置添加元素,即
elements[(front+size)%elements.length]=77
。代码如下:
if (index == size) {
elements[index(size)] = element;
}
1.3、当向数组中其他位置添加元素
- 如果index大于等于size的一半,则将元素向后移动,然后设置指定位置的元素;如:向index=3,添加元素
image.png - 如果index小于size的一半,则将元素向前移动,并将front减一,然后设置指定位置的元素;
image.png
具体代码如下:
if (index >= size >> 1) { // 向后移动
for (int i = size - 1; i >= index; i--) {
elements[index(i + 1)] = elements[index(i)];
}
elements[index(index)] = element;
} else { // 向前移动
for (int i = 0; i < index; i++) {
elements[index(i-1)] = elements[index(i)];
}
front = index(-1);
elements[index(index)] = element;
}
从上面代码可知,在向数组头部添加元素其实也是属于index<size>>1的情况,只不过不需要移动元素;
在向数组尾部添加元素属于index>=size>>1的情况,只不过不需要移动元素。
1.4、add方法完整代码如下
public void add(int index, E element) {
checkIndexForAdd(index);
ensureCapacity(size + 1);
if (index >= size >> 1) { // 向后移动
for (int i = size - 1; i >= index; i--) {
elements[index(i + 1)] = elements[index(i)];
}
} else { // 向前移动
for (int i = 0; i < index; i++) {
elements[index(i-1)] = elements[index(i)];
}
front = index(-1);
}
elements[index(index)] = element;
size++;
}
// 修正index
private int index(int index) {
index += front;
if (index < 0)
index += elements.length;
else
index = index % elements.length;
return index;
}
2、ArrayList的remove方法
2.1、删除数组头部元素
删除数组头部元素很简单,只需要将头部的数据置成null,然后将front向后移动一位即可。
image.png
代码如下
if (index == 0) {
elements[front] = null;
front = index(1);
}
2.2、删除数组尾部元素
删除尾部元素和动态数组完全一样,直接将index=size-1位置的元素置成null即可。
代码如下
if (index == size - 1) {
elements[index(size - 1)] = null;
}
2.3、删除其他位置的元素
- 如果index大于等于size的一半,则将元素向前移动,然后设置指定位置的元素;如:向index=3,添加元素
image.png - 如果index小于size的一半,则将元素向后移动,并将front加一;
如删除index=2的元素
image.png
代码如下:
if (index >= size >> 1) { // 向前移动
for (int i = index; i < size - 1; i++) {
elements[index(i)] = elements[index(i + 1)];
}
elements[index(size - 1)] = null;
} else { // 向后移动
for (int i = index; i > 0; i--) {
elements[index(i)] = elements[index(i - 1)];
}
elements[front] = null;
front = index(1);
}
2.4、remove的完整代码如下
public E remove(int index) {
checkIndex(index);
E oldE = elements[index(index)];
if (index >= size >> 1) { // 向前移动
for (int i = index; i < size - 1; i++) {
elements[index(i)] = elements[index(i + 1)];
}
elements[index(size - 1)] = null;
} else { // 向后移动
for (int i = index; i > 0; i--) {
elements[index(i)] = elements[index(i - 1)];
}
elements[front] = null;
front = index(1);
}
size--;
if (size == 0)
front = 0;
trim();
return oldE;
}
3、总结
加入front后的动态数组的增删改查逻辑其实也很简单,在编写逻辑时先认为front不存在,然后按照普通动态数组的逻辑编写代码,最后将牵扯到的index位置的地方,使用我们编写的矫正index的方法对index进行矫正,获取其真实的index即可。
完整代码如下:
public class ArrayList2<E> extends AbstractList<E> {
private E[] elements;
private static final int DEFAULT_CAPACTITY = 10;
// 记录数组中首元素的位置,默认为0
private int front;
public ArrayList2() {
this(DEFAULT_CAPACTITY);
}
public ArrayList2(int capacity) {
if (capacity <= DEFAULT_CAPACTITY)
capacity = DEFAULT_CAPACTITY;
elements = (E[]) new Object[capacity];
}
/**
* 向指定位置添加元素
*
* @param index
* @param element
*/
public void add(int index, E element) {
checkIndexForAdd(index);
ensureCapacity(size + 1);
if (index >= size >> 1) { // 向后移动
for (int i = size - 1; i >= index; i--) {
elements[index(i + 1)] = elements[index(i)];
}
} else { // 向前移动
for (int i = 0; i < index; i++) {
elements[index(i - 1)] = elements[index(i)];
}
front = index(-1);
}
elements[index(index)] = element;
size++;
}
// 修正index
private int index(int index) {
index += front;
if (index < 0)
index += elements.length;
else
index = index % elements.length;
return index;
}
/**
* 扩容
*
* @param capactity
*/
private void ensureCapacity(int capactity) {
if (capactity >= elements.length) {
int newCapacity = capactity + (capactity >> 1);
System.out.println("扩容 oldCapactity:" + elements.length + " newCapacity:" + newCapacity);
E[] newElements = (E[]) new Object[newCapacity];
for (int i = 0; i < size; i++) {
newElements[i] = elements[index(i)];
}
elements = newElements;
front = 0;
}
}
/**
* 获取指定位置的元素
*
* @param index
* @return
*/
public E get(int index) {
checkIndex(index);
return elements[index(index)];
}
/**
* 设置index位置的元素
*
* @param index
* @param element
*/
@Override
public void set(int index, E element) {
checkIndex(index);
elements[index(index)] = element;
}
/**
* 删除指定位置的元素
*
* @param index
* @return
*/
public E remove(int index) {
checkIndex(index);
E oldE = elements[index(index)];
if (index >= size >> 1) { // 向前移动
for (int i = index; i < size - 1; i++) {
elements[index(i)] = elements[index(i + 1)];
}
elements[index(size - 1)] = null;
} else { // 向后移动
for (int i = index; i > 0; i--) {
elements[index(i)] = elements[index(i - 1)];
}
elements[front] = null;
front = index(1);
}
size--;
if (size == 0)
front = 0;
trim();
return oldE;
}
/**
* 缩容:当size==capactity/2时就进行缩容,缩小为容量的一半
*/
private void trim() {
int newCapacity = elements.length >> 1;
if (size <= newCapacity && elements.length > DEFAULT_CAPACTITY) {
E[] newElement = (E[]) new Object[newCapacity];
for (int i = 0; i < size; i++) {
newElement[i] = elements[index(i)];
}
System.out.println(elements.length + "缩容为" + newCapacity);
front = 0;
elements = newElement;
}
}
/**
* 删除元素
*
* @param element
* @return
*/
public E remove(E element) {
return remove(indexOf(element));
}
/**
* 返回指定元素的位置
*
* @param element
* @return 返回-1,表示未找到元素
*/
public int indexOf(E element) {
for (int i = 0; i < size; i++) {
if (element == null) {
if (elements[index(i)] == null)
return i;
} else {
if (element.equals(elements[index(i)]))
return i;
}
}
return -1;
}
/**
* 清空元素
*/
public void clear() {
for (E e : elements)
e = null;
size = 0;
front = 0;
// 缩容
if (elements != null && elements.length > DEFAULT_CAPACTITY)
elements = (E[]) new Object[DEFAULT_CAPACTITY];
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("size=" + size + " capactity=" + elements.length + " front=" + front + " [");
for (int i = 0; i < elements.length; i++) {
sb.append(elements[i]);
if (i != elements.length - 1)
sb.append(",");
}
sb.append("]");
return sb.toString();
}
}
网友评论