List接口的实现类
在标准的JDK集合框架中,对List
接口的两个重要实现就是ArrayList
和LinkedList
,接下来依次对这两个类进行分析。
ArrayList
ArrayList的继承关系
ArrayList
底层通过数组来实现List
接口。该类通过继承AbstractList
,而后者又继承自AbstractCollection
抽象类,AbstractCollection
抽象类是对Collection
接口的初步实现,而AbstractList
类又对List
接口中新添加的部分方法做了初步实现。这里面对继承关系做一点分析:
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {...}
AbstractList
类的声明中通过
extends AbstractCollection<E>
就可以复用AbstractCollection
中对Collection
接口中部分方法实现,通过
implements List<E>
来完成接口List
中相对与Collection
接口增添的部分方法。
ArrayList源码分析
首先分析ArrayList
的几个构造函数
ArrayList()
ArrayList(int initialCapacity)
ArrayList(Collection<? extends E> c)
对于构造函数1,使用默认的数组size值初始化ArrayList
中的数组对象elementData
private static final int DEFAULT_CAPACITY = 10;
transient Object[] elementData
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
构造函数2与构造函数1类似。最后一种构造函数,函数体中包含了下面这行代码:
elementData = c.toArray();
查看Collection
中的一个实现类ArrayList
,其对一个的toArray()
方法实现如下:
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
而Arrays.copyOf(…)
函数的逻辑实现中复制过程是浅复制,可以通过测试代码进一步验证:
List<String> origin=new ArrayList<>();
origin.add(new String("java"));
List<String> curr=new ArrayList<>(origin);
System.out.println(curr.get(0)==origin.get(0));//true
这种复制的方式存在于ArrayList
的clone()
,toArray()
,toArray(T[] a)
中。
添加和删除相关的方法使用了一些辅助函数实现,此处仅讨论以下函数:
removeAll(Collection<?> c)
iterator()
listIterator()
removeAll(Collection<?> c)
,该函数通过内部辅助函数batchRemove(…)
把elementData
在c中出现的元素全部删除。主要一点是,执行删除之后,elementData
的长度未变,尽管size
的值发生了变化。
对于后面两个关于迭代器的方法,ArrayList
类并未使用AbstractList
中的对应实现,不过也使用了类似的方式,使用了内部类Itr
与内部类ListItr
来实现,其细节分析可以参考我的另一篇博客:AbstaractList类中的listIterator()的实现分析。
重新实现这两个方法的原因其实也比较直观,在已知ArrayList
使用了数组实现数据结构,自然可以采取更加高效的方式来迭代。对于ArrayList
而言,在这两个函数的实现中,很多在AbstractList
通过get(int index)
获得的值,现在可以直接通过随机读取elementData
来完成。
ArrayList中的视图机制
在ArrayList
中存在着多个函数,对同一个底层对象elementData
构造不同的新的对象,而这些新的对象中的操作是通过ArrayList
对象中的方法实现的,这些新的对象可以称为原ArrayList
对象的视图。ArrayList
中包含了如下视图:
List subList(int fromIndex, int toIndex)
该方法返回一个原列表的子列表。以子列表中的get(int index)
实现分析:
public E get(int index) {
Objects.checkIndex(index, size);
checkForComodification();
return root.elementData(offset + index);//root指的就是原ArrayList对象
}
其中最后一行代码就是调用了原ArrayList
对象中的elementData(int index)
方法来完成视图对象的逻辑。其他的方法实现也是类似的。
LinkedList
LinkedList的继承关系
为了便于LinkedList
的实现,JDK并未像ArrayList
一样直接从AbstractList
继承,而是继承自抽象类AbstractSequentialList
,而该类继承AbstractList
。引用JDK文档中的一段话,更好地说明AbstractSequentialList
与AbstractList
的关系。
This class is the opposite of the AbstractList class in the sense that it implements the “random access” methods (get(int index), set(int index, E element), add(int index, E element) and remove(int index)) on top of the list’s list iterator, instead of the other way around.
AbstractSequentialList
中的iterator()
函数值得注意,实现代码为:
public Iterator<E> iterator() {
return listIterator();
}
说明它的子类无论是调用iterator()
还是listIterator()
,返回的迭代器都是双向的。
LinkedList源码分析
LinkedList
类使用链表作为其底层的数据结构,其节点类即为嵌套类Node
,其定义如下:
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
LinkedList
类保存了对链表的头引用(first
)与尾引用(last
),一个原因是LinkedList
也是Deque
接口的子类,这样更容易实现对应的方法。
和ArrayList
的分析顺序类似,首先分析它的构造器:
LinkedList()
LinkedList(Collection<? extends E> c)
第一个构造器生成的为空的列表,第二个构造器的首先调用了函数addAll(int index, Collection<? extends E> c)
,其中的主要逻辑代码为:
for (Object o : a) {
@SuppressWarnings("unchecked") E e = (E) o;
Node<E> newNode = new Node<>(pred, e, null);
if (pred == null)
first = newNode;
else
pred.next = newNode;
pred = newNode;
}
这样,就把一个Collection
中的元素构成了链接关系。其它的增添或者修改方法和数据结构中的链表操作类似,此处省略。
另外,在LinkedList
类中,也实现了Deque
接口中一些方法,如:
E peek()
E element()
E poll()
E remove()
boolean offer(E e)
- …
其具体功能与实现细节参考JDK源码实现即可,此处就不赘述。
最后,在LinkedList
类中,其iterator()
方法并未重写,只是对listIterator()
方法进行了重写,其目的是可以针对链表结构优化AbstractList
中的实现。由于AbstractSequentialList
在iterator()
方法体中调用并返回了listIterator()
的值。所以没必要重写iterator()
方法了。
这就是List
接口的来生,其前世在上一篇博客 java集合 List接口(上)。