java集合 List接口(下)

List接口的实现类

  在标准的JDK集合框架中,对List接口的两个重要实现就是ArrayListLinkedList,接下来依次对这两个类进行分析。

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的几个构造函数

  1. ArrayList()
  2. ArrayList(int initialCapacity)
  3. 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

这种复制的方式存在于ArrayListclone()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文档中的一段话,更好地说明AbstractSequentialListAbstractList的关系。

  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中的实现。由于AbstractSequentialListiterator()方法体中调用并返回了listIterator()的值。所以没必要重写iterator()方法了。
      这就是List接口的来生,其前世在上一篇博客 java集合 List接口(上)
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值