List及其实现类

List

List是Java集合Collection的重要组成部分。代表了有序的集合,也可以称为序列。对于一个有序序列,我们可以精确的控制每一个元素的位置,并通过元素的整数索引(索引为非负)来访问和检索元素。

         对于序列来说,允许存在重复的元素,严格一些来说就是允许equals方法返回true的元素存在,同时也允许NULL值的存在,这样就会有一个很有趣的现象,即一个序列中存在有很多个null这个是很常见的,但是一般这种情况下不同的null可能会有不同的含义,实际项目中建议尝试使用空对象来处理,可能会有意想不到的效果。

         List,重写了equals和hashCode方法。鉴于序列具有有序的特性,所以只有每一个位置上的元素都相等equals才会返回true。所以hashCode的计算也是同每一个元素相关联的。

    List提供了迭代器Iterator,可以用来浏览所有集合元素。但是List也提供一个特殊的迭代器(ListIterator),该迭代器不光可以用来对所有元素进行迭代,也可以对List进行插入、修改和删除操作。所有子类都是用它对元素进行处理。

AbstractList

         AbstractList是List接口的一个很重要的实现抽象类。最大程度的减少了实现由“随即访问”(如数组)数据存储支持的接口的工作。从它的迭代器可以看到:

public E next() {
        checkForComodification();
        try {
            int i = cursor;
            E next = get(i);
            lastRet = i;
            cursor = i + 1;
            return next;
        } catch (IndexOutOfBoundsException e) {
            checkForComodification();
            throw new NoSuchElementException();
        }
}


可见,在操作元素时,迭代器是用get方法通过index位置来查找的,在实现子类中我们要实现该方法,可以是从数组中查取也可以是链表中查取,这取决于数据的存储方式。

要实现不可修改的列表,程序员只需扩展此类,并提供 get(int index) 和 size() 方法的实现。

  要实现可修改的列表,程序员还必须另外重写 set(int index,Object element) 方法,否则将抛出UnsupportedOperationException。如果列表为可变大小,则程序员必须另外重写add(int index,Object element) 和 remove(int index) 方法。

public abstract class AbstractList<E> extends AbstractCollection<E>implements List<E> {
    ……
    public int hashCode() {
        int hashCode = 1;
        for (E e : this)
            hashCode = 31*hashCode + (e==null ? 0 :e.hashCode());
        return hashCode;
}
}


    由上面的hashCode的计算方式也可以看到,对于两个元素相同但是位置不同的序列,尽管它们的equals返回false,但是它们的hashCode是相同的。所以散列一组序列时有可能发生所有的元素都置于同一个桶上的情况,可见这种hashCode的计算方式不是很美好,可以说是挺糟糕的,但是又能怎么办呢?除非哈希值计算时考虑位置加权,否则……

ArrayList

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess,Cloneable, java.io.Serializable{
    ……
}


进一步实现了AbstractList,数据采用数组的方式来存储,对应的读取、删除等操作也是依托于数组的。尽管我们使用了泛型技术,但数据是保存在一个Object数组的,读取时强制转换数据类型。

充分利用了数组在随即读取时的优点,get、set方法都可以以固定时间来完成,但是又有数组所没有的长处,就是一个数组的大小一旦设定就没法再更改,而ArrayList的大小是可以自动变化的,ArrayList是要比单纯的使用数组方便的,尽量要用ArrayList来替代数组,尽管速度上会有一些降低。

Arraylist初始默认大小为10,在add操作时,会在加入元素前判断是否有足够的空间存放,如果没有则会进行扩容操作,会创建一个更大的数组并将现有数组复制。可见在add操作时并不一定会是固定时间,尤其是数据量很大时,复制操作会消耗大量的时间,这种情况下ArrayList不是很好的选择。

而delete操作对于ArrayList来说可以说是一场噩梦。究其原因在于List要求所有的数据都要顺序存储,这样就要求某一位置上的元素在删除后,它后面的元素都必须向前移动一位,这样就要浪费大量的时间来进行移动操作,可想而知效率……

由上可见,ArrayList在随机的读取、修改的效率确实很高,但是增加、删除操作的性能上就差强人意了。

最后,要注意ArrayList不是线程安全的。可以通过以下的操作,确保同步操作

List list =Collections.synchronizedList(new ArrayList(...));


    注意。接口RandomAccess ,没有任何具体内容,只是用来表明其支持快速(通常是固定时间)随机访问,就像是Cloneable接口。我们可以通过instanceOf来确定某个List是否支持快速的随机访问,来确定我们应该用什么处理方式来处理这个List,这个在优化效率上还是很有帮助的。

 

AbstractSequentialList

         AbstractSequentialList是List的一个骨干抽象类。最大程度的减少了实现由“连续访问”(如链表)数据存储支持的接口工作。

         是AbstractList的子类,实现了get、add、remove方法,所有这些实现依赖于ListIterator。继承实现该类时,需要根据实际情况重写该类listIterator方法。

    该类就是为了实现连续访问而设计的。尽管也可以采用数组的方式来实现该类,可迭代器就是一种逐位操作的方式,这样的话数组就需要采用顺序的方式来处理,但数组随即读取的特点就发挥不出来了,无法发挥出它应有的优点。这里我们需要考虑一种更符合连续访问的数据结构,就是链表。

public abstract class AbstractSequentialList<E> extendsAbstractList<E>{
    ……
}


LinkedList

public class LinkedList<E>
    extendsAbstractSequentialList<E>
implements List<E>, Deque<E>,Cloneable, java.io.Serializable{
……
}


    接口Deque表明,我们可以像操作一个队列一样来操作LinkedList,利用程序提供的poll和push方法,可以将队列是构建为一个先进先出的队列。同样也可以利用LinkedList提供的其他相关方法来实现堆栈或是双端队列。

    顾名思义LinkedList是使用一个链表来实现数据存储的。该链表是一个双向链表,每一个节点记录了前一个节点和后一个节点。

    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在add和remove操作时,所需要做的就是增加或是删除前后节点的连接关系,操作同前后相邻的节点都有关系。但是由于不要求所有数据存储在一个连续的空间上,所以数据一旦存储后不像ArrayList还需要根据情况来调整,这样时间复杂度就降低为一个常数。

    但是get、set操作就不一样了,由于每次我们查找一个数据都需要从链表的头上逐个查找,需要花费很多额外的时间。

    LinkedList和ArrayL一样也不是线程安全的。线程安全的处理方式也大体相同。

    所以,LinkedList与ArrayList正好相反,对List的增加、删除操作性能要优于查询操作。在实际使用中要多加注意,不过就我自己的项目经验,LinkedList只是特殊情况下才会使用,大多数情况都是使用ArrayList。

Vector

         Vector类似于ArrayList,但是它是现成安全的。Vector已废弃,主要是在一些旧的代码中才会使用。

public class Vector<E>extends AbstractList<E> implementsList<E>, RandomAccess, Cloneable, java.io.Serializable{
    ……
}


Stack

         Stack表示一个先进先出(FIFO)的堆栈。是由Vector来实现的。但是Queue接口已提供类似的poll和push方法,并在LinkedList中实现了堆栈的功能,所以使用中尽量多的使用LinkedList,而不是Stack。

         Public class Stack<E> extends Vector<E> {
        ……
    }


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值