一、ArrayList和LinkList实现的比较
1.使用get()获取元素
1.1 ArrayList.get()
如果希望使用ArrayList
的get(int index)
方法获取元素,实现可以简单地将这项任务委托给其内部数组:
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
当然,还会对给定索引执行额外检查(确保它不小于零或大于数组大小)。
我们可以看到这个操作是在常数时间内执行的,即O(1)。这意味着无论数组大小如何,任何请求的元素都将立即返回,而不需要遍历列表。这是因为整个数组存储在内存中的一个唯一位置。
第二个元素的槽正好位于第一个元素之后,第n个元素的槽正好位于第 n+1 个元素之前。依靠这个内部结构,任何元素都可以很容易地通过索引来获取。
1.2.LinkedList.get()
如果希望从 a 中获取元素LinkedList
,可以使用该get(int index)
方法,但效率很低。
前面我们提到过链表并不存在于内存中的单个位置,而是包含相互连接的不同节点。要获取元素,需要从头(或尾,以较接近者为准)遍历列表,并跟踪每个节点的连接,直到找到所需的元素。
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
private void checkElementIndex(int index) {
if (!isElementIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private boolean isElementIndex(int index) {
return index >= 0 && index < size;
}
Node<E> node(int index) {
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
首先,进行检查以确保索引不大于
0
或大于LinkedList
. 然后,该node()
方法遍历列表,直到遇到我们要搜索的列表。与
ArrayList
的O(1)时间相比,这是在O(N)时间内完成的。`
2.使用add()插入元素
本质上,任何类型的插入都可以使用一种通用方法来概括和实现 - 在给定索引处插入。
如果需要在开头插入元素,可以使用索引0
调用该方法。如果需要在末尾插入元素,则索引将对应于列表的当前大小。如果需要在中间某处插入元素,则用户必须提供该索引。
2.1ArrayList.add()
在末尾插入元素相当简单,特别是对于像ArrayList
. 您只需将长度延长一,然后将元素插入到末尾:
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++;
}
复制的部分越大,此操作就越慢。这使得添加元素的ArrayList
操作效率相对较低。然而,到达应该完成插入的位置确实非常有效。
2.2LinkedList.add()
LinkedList
的实现允许我们相当容易地在任何给定索引处添加元素。您只需将前一个元素和后一个元素的head
指针tail
分别指向新元素即可。如果您在列表的开头或末尾插入,则只需要更新一个指针。
public boolean add(E e) {
linkLast(e);
return true;
}
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
无论列表有多大,只需要更改两个指针。这使得添加元素成为一种
LinkedList
高效的操作。然而,到达应该插入元素的位置是低效的。
3.使用indexOf0查找元素
查找列表中的元素(无论是ArrayList
还是 LinkedList
) 应该非常相似。这是因为除非数组已排序且均匀分布,否则无法先验地知道任何特定元素的存储位置。
列表只是记录其元素并提供操作它们的方法。为了准确地知道每个元素的位置,两种实现都需要经历某种迭代过程,直到找到该元素。
3.1ArrayList.indexOf()
在ArrayList实现中,这是通过一个简单的for
循环从到0
到size-1
并检查当前索引处的元素是否与给定值匹配来完成的:
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;
}
这实际上是一种线性搜索,效率不是很高,但实际上是在打乱的集合中搜索元素的唯一方法(如果我们忽略元启发式算法和近似值)。
3.2LinkedList.indexOf()
LinkedList
有些不同。它不必遍历数组,而是必须使用指针从一个元素跳转到下一个元素来遍历列表。最终,结果是相同的 - 逐一访问每个元素,直到找到要搜索的元素:
public int indexOf(Object o) {
int index = 0;
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null)
return index;
index++;
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item))
return index;
index++;
}
}
return -1;
}
复制的部分越大,此操作就越慢。同样,这使得
ArrayList
从操作中删除元素变得低效。不过,ArrayList
的一个好处是您可以非常轻松地获得该元素。elementData(index)
返回您希望在O(1)时间内删除的元素。
4.使用rremove()删除元素
4.1ArrayList.remove()
与在给定索引处添加元素非常相似,删除它们需要ArrayList
复制自身的一部分并重新初始化没有值的数组,将复制的部分向左移动:
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;
}
复制的部分越大,此操作就越慢。同样,这使得
ArrayList
从操作中删除元素变得低效。不过,ArrayLists
的一个好处是您可以非常轻松地获得该元素。elementData(index)
返回您希望在O(1)时间`内删除的元素。
4.2LinkedList.remove()
LinkedList
通过取消我们要删除的元素的前一个和后一个指针的链接,可以从 a 中删除一个元素。之后,前一个元素链接到行中的下一个元素。这样,旧元素就“搁浅”了,没有对其的引用,GC 会处理它:
public boolean remove(Object o) {
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {
unlink(x);
return true;
}
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}
标签:index,return,int,ArrayList,元素,LinkList,比较,size From: https://www.cnblogs.com/reubenche/p/18344984这使得
LinkedList
删除元素的操作变得``高效,因为同样只需要更改几个点。不过,列表越长,到达需要删除的元素所需的时间就越长,因为我们无法通过索引访问元素。