zoukankan      html  css  js  c++  java
  • Java集合总结(一):列表和队列

    java中的具体容器类都不是从头构建的,他们都继承了一些抽象容器类。这些抽象容器类,提供了容器接口的部分实现,方便具体容器类在抽象类的基础上做具体实现。容器类和接口的关系架构图如下:

    虚线框表示接口,有Collection, List, Set, Queue, Deque和Map。

    有六个抽象容器类:

    • AbstractCollection: 实现了Collection接口,被抽象类AbstractList, AbstractSet, AbstractQueue继承,ArrayDeque也继承自AbstractCollection (图中未画出)。
    • AbstractList:父类是AbstractCollection,实现了List接口,被ArrayList, AbstractSequentialList继承。
    • AbstractSequentialList:父类是AbstractList,被LinkedList继承。
    • AbstractMap:实现了Map接口,被TreeMap, HashMap, EnumMap继承。
    • AbstractSet:父类是AbstractCollection,实现了Set接口,被HashSet, TreeSet和EnumSet继承。
    • AbstractQueue:父类是AbstractCollection,实现了Queue接口,被PriorityQueue继承。

    这些抽象类都提供了相应接口的基础实现,同时定义了一些子类必须重写的抽象方法 ,相当于定义了一个模板,这有点类似于一个设计模式——模板方法模式

    下面主要总结一下上图中具体实现类的一些细节和特性:

    ArrayList

    内部使用Object数组实现、默认初始大小是10,可手动指定,扩容策略是 newCapacity = oldCapacity + (oldCapacity >> 1),
    即每次增大到原来的1.5倍,但不会小于最小值,最大容量是Integer.MAX_VALUE,即0x7fffffff。扩容时将一个新建一个数组将原来数组的内容复制过去。

    没用泛型数组E[]原因个人猜测是因为java中的泛型是在jdk1.5才开始支持的,在这之前都用的Object,为了保持向前的兼容性不得不这么做,jdk1.6中新增的ArrayDeque内部就是泛型数组E[]。

    LinkedList
    内部使用链表实现,可以用作队列(Queue),队列两端都可以操作,尾部添加,头部查看和删除。也可以用作双端队列(Deque)(双向链表)(双向遍历、可以实现栈的功能)。

    内部只维护头和尾结点,构造方法不能指定大小,按需分配空间。

    性能

    • 不可以随机访问,必须从头或尾开始查找,效率为O(N/2)(用了二分法)
    • 按照内容查找效率为O(N/2)
    • 两端添加删除效率为O(1)
    • 中间插入删除先定位 ,效率为O(n),但插入或者删除本身效率很高,为O(1)

    ArrayDeque
    这是jdk1.6新增的一个类。内部维护一个泛型数组E[],使用head和tail两个int索引表示链表头和尾(其中tail指向下一个空位),使得物理上简单的从头到尾的数组变成了逻辑上循环的数组,可以作为链表使用。默认初始数组大小是16,扩容策略是乘以2

    • 如果head和tail相同,则数组为空,长度为0。
    • 如果tail大于head,则第一个元素为elements[head],最后一个为elements[tail-1],长度为tail-head,元素索引从head到tail-1。
    • 如果tail小于head,且为0,则第一个元素为elements[head],最后一个为elements[elements.length-1],元素索引从head到elements.length-1。
    • 如果tail小于head,且大于0,则会形成循环,第一个元素为elements[head],最后一个是elements[tail-1],元素索引从head到elements.length-1,然后再从0到tail-1。

    可以通过构造方法指定初始数组大小numElements,但是实际的大小是:

    • 如果numElements小于MIN_INITIAL_CAPACITY,则分配的数组长度就是MIN_INITIAL_CAPACITY,它是一个静态常量,值为8。
    • 在numElements大于等于8的情况下,分配的实际长度是严格大于numElements并且为2的整数次幂的最小数。比如,如果numElements为10,则实际分配16,如果numElements为32,则为64。

    使用2的幂次方是为了保证

    public void addLast(E e) {
            if (e == null)
                throw new NullPointerException();
            elements[tail] = e;
            if ( (tail = (tail + 1) & (elements.length - 1)) == head)
                doubleCapacity();
        }

    (tail + 1) & (elements.length - 1)能定位到正确下一个位置tail,比如说,如果elements.length为8,则(elements.length - 1)为7,二进制为0111,对于负数-1,与7相与,结果为7,对于正数8,与7相与,结果为0,都能达到循环数组中找下一个正确位置的目的。

    扩容之后会重新排列数组,head是0,tail是当前元素数。

    性能

    • 在两端添加、删除元素的效率很高,动态扩展需要的内存分配以及数组拷贝开销可以被平摊,具体来说,添加N个元素的效率为O(N)。
    • 根据元素内容查找和删除的效率比较低,为O(N)。

    简单总结:

    与ArrayList和LinkedList不同,ArrayDeque没有索引位置的概念,不能根据索引位置进行操作。
    ArrayDeque和LinkedList都实现了Deque接口,应该用哪一个呢?如果只需要Deque接口,从两端进行操作,一般而言,ArrayDeque效率更高一些,应该被优先使用,不过,如果同时需要根据索引位置进行操作,或者经常需要在中间进行插入和删除,则应该选LinkedList。

  • 相关阅读:
    WebRTC相关技术预研总结
    What is "jar.mn"?
    (FFOS Gecko & Gaia) OTA
    (FFOS Gecko & Gaia) OTA
    (FFOS Gecko & Gaia) OTA
    (FFOS Gecko & Gaia) OTA
    (FFOS Gecko & Gaia) OTA
    (FFOS Gecko & Gaia) OTA
    (FFOS Gecko & Gaia) OTA
    EF实体框架 5 性能注意事项
  • 原文地址:https://www.cnblogs.com/JackPn/p/9419349.html
Copyright © 2011-2022 走看看