通常。Tree是Tree,List是List,两者不太可能混在一起。但apache-commons库却用tree实现了实现了List的接口,也就是TreeList类。与标准的LinkedList相比。TreeList略微浪费一点空间,但经常使用操作的时间复杂度均减少到了O(log N),值得在开发中权衡利弊、合理应用。
内部数据结构
TreeList内部包括了一个Thread AVL Tree。AVL Tree非经常见了,是一种典型的Balanced Binary Tree,但以下简介下Thread Binary Tree。
Thread Binary Tree对Binary Tree添加了下面特性:(1)假设一个节点X没有左子树。则把本来应指向左子树的指针,指向中序遍历的前节点。(2)假设一个节点X没有右子树,则把本来应指向右子树的指针,指向中序遍历的后节点。
下图就是一个Thread Binary Tree,以节点5为例:(1)
节点5没有左子树,但节点5的中序遍历的前节点是4;(2)
节点5没有右子树,但节点5的中序遍历的后节点是6。
这两个特性提高了二叉树依序訪问的速度。
下面是TreeList中AVL树节点的定义
static class AVLNode<E> {
/** 左子树或者中序遍历的前节点.*/
private AVLNode<E> left;
/** true表示left字段是左子树;false表示left字段是中序遍历的前节点 */
private boolean leftIsPrevious;
/** 右子树或者中序遍历的后节点 */
private AVLNode<E> right;
/** true表示right字段是右子树;false表示right字段是中序遍历的后节点 */
private boolean rightIsNext;
/** How many levels of left/right are below this one. */
private int height;
/** 在List中的索引相对于父节点索引的偏移量。根节点就是根节点的索引*/
private int relativePosition;
/** 节点所保存的有效荷载 */
private E value;
}
在逻辑上。TreeList中的节点是依据节点在List中的索引来比較大小的。在实现上,AVLNode类保存的是当前节点的索引相对于父节点的偏移量,也就是relativePosition这个字段。这样做的长处是,当向List中间插入一个节点时。插入点之后的全部节点的索引值都变大了,但由于AVLNode保存的是相对值。因此仅仅须要改动特定子树的根节点的relativePosition值,整个子树全部节点的索引值都会发生变化。
时空复杂度
TreeList既然是用AVL树实现,则其在特定位置进行插入、删除和get操作的时间复杂度都是O(log N),另外还要加上较大的时间常量。
LinkedList是採用双向链表实现的。其在特定位置进行插入、删除和get操作的时间复杂度都是O(N)。
空间复杂度
首先看一下LinkedList中每一个节点的定义:
private static class Entry<E> {
E element;
Entry<E> next;
Entry<E> previous;
}
依据以上定义。在32位Hostspot虚拟机下,每一个Entry对象占用6*4=24个byte(这包含8个byte的对象头、12个byte的真实字段和4个byte的对齐填充)。
依据AVLNode的定义,每一个AVLNode节点占用8*4=32个byte(包含8个byte的对象头、22个byte的真实字段和2个byte的对齐填充)。
因此,TreeList的每一个节点比LinkedLists多占领8个byte。