Java容器之List
阿新 • • 發佈:2020-12-24
1、Java集合概覽
Java的容器有List、Queue、Set、Map等。從上圖可以看出,除了以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; publicclass 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() } }