1. 程式人生 > 實用技巧 >Java容器之List

Java容器之List

1、Java集合概覽

Java的容器有ListQueueSetMap等。從上圖可以看出,除了以Map結尾的類之外,其他類都實現了Collection介面。以Map結尾的類實現了Map介面。

2、說一說這些容器都有什麼區別?

  • List:儲存的元素是有序的、可重複的。
  • Set:儲存的元素是無序的、不可重複的。
  • Queue:FIFO(先進先出)
  • Map:使用鍵值對(key-value)儲存,類似於數學上的函式 y=f(x),“x”代表 key,"y"代表 value,Key 是無序的、不可重複的;value 是無序的、可重複的,每個鍵最多對映到一個值。

3、List底層的資料結構

  • ArrayList:Object[](陣列),是List的主要實現類,執行緒不安全。
  • Vector:Object[],是List的古老實現類。與ArrayList不同的是它支援執行緒的同步,即某一時刻只有一個執行緒能夠寫Vector,避免多執行緒同時寫而引起的不一致性,但實現同步需要很高的花費,因此,訪問它比訪問ArrayList慢,一般也不推薦使用。
  • LinkedList:雙向連結串列

4、ArrayList和LinkedList的區別?

5、ArrayList實現程式碼

package JavaContainer;
/*ArrayList泛型類的實現*/
import java.util.Arrays;
import java.util.Iterator;

public
class MyList<AnyType> implements Iterable<AnyType> { //list 預設初始容量 private final static int DEFAULT_CAPACITY = 10; //MyList包含元素的個數 private int theSize; //theItems陣列 private AnyType[] theItems; //空陣列 private static final Object[] EMPTY_ELEMENTDATA = {}; //用於預設大小的共享空陣列例項,以知道在新增第一個元素時容量需要增加多少。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; //儲存ArrayList資料的陣列 transient Object[] elementData; //得到最小擴容量 private void ensureCapacityInternal(int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { // 獲取“預設的容量”和“傳入引數”兩者之間的最大值 minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); } /**判斷是否需要擴容**/ private void ensureExplicitCapacity(int minCapacity) { //modCount++; 擴容次數 if (minCapacity - elementData.length > 0) //呼叫grow方法進行擴容,呼叫此方法代表已經開始擴容了 grow(minCapacity); } /**要分配的最大陣列大小**/ private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; /**ArrayList擴容的核心方法**/ private void grow(int minCapacity) { // oldCapacity為舊容量,newCapacity為新容量 int oldCapacity = elementData.length; //將新容量更新為舊容量的1.5倍(位運算速度快) int newCapacity = oldCapacity + (oldCapacity >> 1); //然後檢查新容量是否大於最小需要容量,若還是小於最小需要容量,那麼就把最小需要容量當作陣列的新容量, if (newCapacity - minCapacity < 0) newCapacity = minCapacity; //再檢查新容量是否超出了ArrayList所定義的最大容量,若超出了,則呼叫hugeCapacity()來比較minCapacity和 MAX_ARRAY_SIZE, //如果minCapacity大於MAX_ARRAY_SIZE,則新容量則為Interger.MAX_VALUE,否則,新容量大小則為 MAX_ARRAY_SIZE。 if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); elementData = Arrays.copyOf(elementData, newCapacity); } //比較minCapacity和 MAX_ARRAY_SIZE private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; } //預設無參構造 public MyList() { doclear(); } //清空MyList public void clear(){ doclear(); } private void doclear() { theSize = 0; ensureCapcity(DEFAULT_CAPACITY); } //擴容,將Mylist的最大容量擴充到newCapcity public void ensureCapcity(int newCapcity) { if (newCapcity < DEFAULT_CAPACITY) return; AnyType[] old = theItems; theItems = (AnyType[]) new Object[newCapcity]; //直接定義泛型的陣列是非法的,只能先用Object定義在轉換 for(int i = 0; i < size(); i++) { theItems[i] = old[i]; } } //返回此list的大小 public int size() { return theSize; } //判斷list是否為空 public boolean isEmpty() { return size() == 0; } //修改Mylist的容量為當前列表的大小,節省儲存空間 public void trimToSize() { ensureCapcity(size()); } //查詢下標為idx的值 public AnyType get(int idx){ if(idx < 0 || idx > size()) throw new ArrayIndexOutOfBoundsException(); return theItems[idx]; } //將下標為idx的元素設定為newVal,並返回覆蓋前的值 public AnyType set(int idx, AnyType newVal) { if(idx < 0 || idx > size()) throw new ArrayIndexOutOfBoundsException(); AnyType old = theItems[idx]; theItems[idx] = newVal; return old; } public boolean add(AnyType x) { //一個引數的add預設新增到list最後,size()表示可以被放置的位置 add(size(), x); return true; } public void add(int idx, AnyType x){ if (idx < 0 || idx > size()) throw new ArrayIndexOutOfBoundsException(); //list的容量滿了,進行擴容 if(theItems.length == size()) ensureCapcity(size() * 2 + 1); //擴容 for(int i = theSize; i > idx; i--) theItems[i] = theItems[i-1]; theItems[idx] = x; theSize++; } //將idex處的元素移除,並返回被移除的元素 public AnyType remove(int idx) { AnyType removeItem = theItems[idx]; for(int i = idx; i < size(); i++) theItems[i] = theItems[i+1]; theSize--; return removeItem; } //內部類ArrayListIterator實現了針對於MyList類的Iterator<AnyType>介面 private class ArrayListIterator implements java.util.Iterator<AnyType> { 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++]; } public void remove() { MyList.this.remove(--current); } } @Override public Iterator<AnyType> iterator() { return new ArrayListIterator(); } public static void main(String[] args) { //test() } }

6、LinkedList的實現程式碼

package JavaContainer;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.NoSuchElementException;

public class MyLinkedList<AnyType> implements Iterable<AnyType> {

    private static class Node<AnyType> {
        public AnyType data;
        public Node<AnyType> prev;  //指向前一個節點的指標
        public Node<AnyType> next;  //指向後一個節點的指標

        //建構函式
        public Node(AnyType x, Node<AnyType> p, Node<AnyType> q){
            data = x; prev = p; next = q;
        }
    }

    //MyLinkedList的資料成員
    private int theSize;
    private int modCount = 0;   //add、remove、clear時才修改
    private Node<AnyType> beginMarker;  //頭結點,作額外的標記
    private Node<AnyType> endMarker;    //尾節點,作額外的標記

    //無參構造
    public MyLinkedList() {
        doclear();
    }

    //清理
    public void clear() {
        doclear();
    }

    private void doclear() {    //只有兩個標記節點
        beginMarker = new Node<AnyType>(null, null,null);
        endMarker = new Node<AnyType>(null, beginMarker,null);

        beginMarker.next = endMarker;
        modCount ++;
        theSize = 0;    //這個theSize指的是有效地theSize,標記節點不算
    }

    //獲取當前MyLinkedList的大小
    public int size() {
        return theSize;
    }
    //判斷當前MyLinkedList是否為空
    public boolean isEmpty() {
        return size() == 0;
    }

    //在尾部新增新元素x
    public boolean add(AnyType x) {
        add(size(), x);
        return true;
    }

    public void add(int idx, AnyType x) {
        Node<AnyType> p = getNode(idx, 0, size()-1);
        addBefore(p ,x);  //先找到這個節點,然後再插入
    }

    //已經確定插入節點的位置,插入節點
    private void addBefore(Node<AnyType> p, AnyType x) {
        Node<AnyType> newNode = new Node<AnyType>(x, p.prev, p);
        newNode.prev.next = newNode;
        p.prev = newNode;

        theSize++;
        modCount++;
    }

    //得到idx節點處的值(而不是引用)
    public AnyType get(int idx) {
        return getNode(idx).data;
    }

    //查詢(在預設範圍內)
    private Node<AnyType> getNode(int idx) {
        return getNode(idx, 0 , size() - 1);
    }

    //查詢(在指定範圍內)
    private Node<AnyType> getNode(int idx, int lower, int upper) {
        Node<AnyType> p;

        if(idx < lower || idx > upper)
            throw new IndexOutOfBoundsException();

        //分成兩部分能提高查詢的效率
        if(idx < size() / 2) {  //前半部分
            p = beginMarker.next;
            for(int i = 0; i < idx; i++) {
                p = p.next;
            }
        }

        else {  //後半部分
            p = endMarker;
            for(int i = size(); i > idx; i--) {
                p = p.prev;
            }
        }
        return p;
    }

    public AnyType set(int idx, AnyType newVal) {
        Node<AnyType> p = getNode(idx);
        AnyType oldVal = p.data;
        p.data = newVal;
        return  oldVal;
    }

    public AnyType remove(int idx) {
        return remove(getNode(idx));
    }
    //remove idx處的節點,並返回刪除的值
    private AnyType remove(Node<AnyType> p) {
        p.prev.next = p.next;
        p.next.prev = p.prev;

        theSize--;
        modCount++;
        //有人可能會有疑問,沒用的節點p佔據的空間沒有釋放,注意因為JVM中有垃圾回收機制,不需要程式設計師手動釋放
        return p.data;
    }

    //MyLinkedList繼承Iterable介面,重寫Iterator方法
    @Override
    public Iterator<AnyType> iterator() {
        return new LinkedListIterator();
    }

    //內部類(迭代器中主要實現三種方法hasNext、next和remove)
    private class LinkedListIterator implements java.util.Iterator<AnyType> {
        private Node<AnyType> current = beginMarker.next;   //當前位置
        /**(之所以要記錄這個是因為:為避免迭代器準備給出某一項作為下一項nextItem,而該項以後可能被刪除,或者一個新的項正好插入在該項的前面,所以必須要記住這些更改法則)**/
        private int expectedModCount = modCount;    //檢查modCount是否正確
        private boolean okToRemove = false;

        @Override
        public boolean hasNext() {
            return current != endMarker;
        }

        @Override
        public AnyType next() { //返回節點的值,然後向後繼續推進current
            if(modCount != expectedModCount)
                throw new ConcurrentModificationException();
            if(!hasNext())
                throw new NoSuchElementException();

            AnyType nextItem = current.data;
            current = current.next;
            okToRemove = true;
            return nextItem;
        }

        public void remove(){
            if(modCount != expectedModCount)
                throw new ConcurrentModificationException();
            if(!okToRemove)
                throw new IllegalStateException();

            MyLinkedList.this.remove(current.prev);
            expectedModCount++; //如果迭代器呼叫了自己的remove方法,那麼這個迭代器仍然是合法的,因為expectedModCount同步更新了
            okToRemove = false;
        }
    }
    public static void main(String[] args) {
        //test()
    }

}