Java集合之List接口

List接口、ArrayList類和LinkedList類

1.List

List接口繼承自Collection接口,其中常用的較爲重要的方法如下:

public interface List<AnyType> extends Collection<AnyType>{
    int size();
    boolean add(E e);
    boolean remove(Object o);
    E get(int index);
    E set(int index, E element);
}

get和set使得用戶可以訪問或改變通過由位置索引index給定的表中指定位置上的項。索引0位於表的前端,索引size()-1代表表中的最後一項。
List有兩種流行的實現方式,ArrayList和LinkedList。

ArrayList、LinkedList

ArrayList類提供了List的一種可增長數組的實現.使用ArrayList的優點在於,對get和set的調用花費常數時間。其缺點是新項的插入和現有項的刪除代價昂貴,除非變動是在ArrayList的末端進行。
LinkedList則提供了List的雙向鏈表實現。使用LinkedList的優點在於,新項的插入和現有項的刪除均開銷很小,這意味着在表的前端進行添加和刪除都是常數時間的操作,由此LinkedList更提供了方法addFirst和removeFirst、addLast和removeLast等方法可以有效的添加、刪除和訪問表兩端的項。使用LInkedList的缺點是它不容易作索引,因此對get的調用是昂貴的,除非調用非常接近表的端點。
首先,我們在表的末端添加一項來構造list:

 public static void makeList1(List<Integer> lst, int N) {
        lst.clear();
        for (int i = 0; i < N; i++) {
            lst.add(i);
        }
    }

不管ArrayList還是LinkedListlist作爲參數進行傳遞,makeList1的運行時間都是o(N),因此對add的每次調用都是在表的末端進行從而花費常數時間。
如果我們從表的前端構造一個List:

public static void makeList2(List<Integer> lst, int N) {
        lst.clear();
        for (int i = 0; i < N; i++) {
            lst.add(0, i);
        }
    }

那麼,對於LinkedList它的運行時間是O(N),但是對於ArrayList其運行時間則是O(N2),因爲在ArrayList中,在前端進行添加是一個O(N)操作。
下一個例子是極端List中的數的和:

public static int makeList3(List<Integer> lst, int N) {
        int total = 0;
        for (int i = 0; i < N; i++) {
            total += lst.get(i);
        }
        return total;
    }

這裏ArrayList的運行時間是O(N),但是對於LinkedList來說,其運行時間則是O(N2),因爲在LinkedList中,對get的調用爲O(N)操作。
如果使用一個增強的for循環,那麼它對任意List的運行時間都是O(N),因爲迭代器將有效地從一項到下一項推進。

將list表中所有偶數值刪除:
因爲ArrayList刪除效率地下,所以在涉及需要進行刪除操作時,我們應該構建LinkedList表。但是在LinkedList中,由於對get調用的效率不高,因此也會花費較多時間,所以remove的效率同樣低下,代碼如下:

public static void removeEvenVer1(List<Integer> lst){
        int i = 0;
        while (i<lst.size()){
            if(lst.get(i)%2==0){
                lst.remove(i);
            }else {
                i++;
            }
        }
    }

如果我們不使用get()方法去獲取數據,而是使用迭代器,會不會變得高效一點呢

    public static void removeEvenVer2(List<Integer> lst){
        for (Integer x:lst){
            if(x%2==0){
                lst.remove(x);
            }
        }
    }

但是我們運行這個程序,會產生一個異常,因爲當一項被刪除時,由增強的for循環所使用的基礎迭代器是非法的。

迭代器使用:
在迭代器找到一個偶數值項之後,我們可以使用該迭代器來刪除這個它剛看到的值。對於一個LinkedList,對該迭代器的remove方法的調用只花費常數時間,因爲該迭代器位於被刪除的節點。因此,對於LinkedList,整個程序花費線性時間,而不是二次時間。對於ArrayList,因爲數組的刪除伴隨着數組項的移動,所以花費的還是二次時間。

public static void removeEvensVer3(List<Integer> lst){
        Iterator<Integer> itr = lst.iterator();
        while (itr.hasNext()){
            if(itr.next()%2 == 0){
                itr.remove();
            }
        }
    }

ArrayList類的實現

package collections.list;

import java.util.Iterator;
import java.util.function.Consumer;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author crazyang
 * @Desciption:
 * @Date 2018-6-8 17:04
 */
public class MyArrayList<AnyType> implements Iterator<AnyType> {

    private static final int DEFAULT_CAPACITY = 10;

    private int theSize;
    private AnyType[] theItems;

    public MyArrayList(){
        doClear();
    }
    public void clear(){
        doClear();
    }
    public void doClear(){
        theSize = 0;
        ensureCapacity(DEFAULT_CAPACITY);
    }
    public int size(){
        return  theSize;
    }
    public boolean isEmpty(){
        return size() == 0;
    }
    public void trimToSize(){
        ensureCapacity(size());
    }

    public AnyType get(int index){
        if(index<0||index>=size()){
            throw new ArrayIndexOutOfBoundsException();
        }
        return theItems[index];
    }
    public AnyType set(int index,AnyType newVal){
        if(index<0||index>=size()){
            throw new ArrayIndexOutOfBoundsException();
        }
        AnyType old = theItems[index];
        theItems[index] = newVal;
        return old;
    }

    public void ensureCapacity(int newCapacity){
        if(newCapacity<theSize){
            return;
        }
        AnyType [] old = theItems;
        theItems=(AnyType[])new Object[newCapacity];
        for(int i =0;i<size();i++){
            theItems[i] = old[i];
        }
    }
    public boolean add(AnyType x){
        add(size(),x);
        return true;
    }
    public void add(int index,AnyType x){
        if(theItems.length == size()){
            ensureCapacity(size()*2+1);
        }
        for(int i = theSize;i>index;i--){
            theItems[i] = theItems[i-1];
        }
        theItems[index] = x;
        theSize++;
    }

    public AnyType remove(int index){
        AnyType removeItem = theItems[index];
        for(int i = index;i<size()-1;i++){
            theItems[i] = theItems[i+1];
        }
        theSize--;
        return removeItem;
    }
    private int current=0;


    @Override
    public boolean hasNext() {
        return current<size();
    }

    @Override
    public AnyType next() {
        if(!hasNext()){
          throw new java.util.NoSuchElementException();  
        }
        return theItems[current++];
    }

    @Override
    public void remove() {
        MyArrayList.this.remove(--current);
    }

}

LinkedList類的實現

LinkedList將作爲雙向鏈表來實現,而且我們還需要保持該表兩端的引用。
設計:
1、MyLinkedList類本身,它包含到兩端的鏈、表的大小以及一些方法。
2、Node類,它可能是一個私有的嵌套類。一個節點包含數據以及到前一個節點的鏈和到下一個節點的鏈,還有一些適當的構造方法。
3、LinkedListIterator類,該類抽象了位置的概念,是一個私有類,並實現接口Iterator。它提供了next、hasNext和remove的實現。

package collections.list;

import java.util.Iterator;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author crazyang
 * @Desciption:
 * @Date 2018-6-8 17:29
 */
public class MyLinkedList<AnyType> implements Iterable<AnyType> {
    private int theSize;
    private int modCount = 0;
    private Node<AnyType> beginMarker;
    private Node<AnyType> endMarker;

    private static class Node<AnyType> {
        public AnyType data;
        public Node<AnyType> prev;
        public Node<AnyType> next;

        public Node(AnyType d, Node<AnyType> p, Node<AnyType> n) {
            data = d;
            prev = p;
            next = n;
        }
    }

    public int size() {
        return theSize;
    }

    public boolean add(AnyType x) {
        add(size(), x);
        return true;
    }

    public void add(int index, AnyType x) {
        addBefore(getNode(index, 0, size()), x);
    }

    public void addBefore(Node<AnyType> p, AnyType x) {
        Node newNode = new Node<>(x, p.prev, p);
        newNode.prev.next = newNode;
        p.prev = newNode;
        theSize++;
        modCount++;
    }

    public Node<AnyType> getNode(int index) {
        return null;
    }

    public Node<AnyType> getNode(int index, int lower, int upper) {
        return null;
    }


    @Override
    public Iterator<AnyType> iterator() {
        return null;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章