資料結構之List實現類
目錄
1.Arraylist
Arraylist作為常用的資料容器,還是有必要知道一些內部的細節。從執行緒安全方面來看,Arraylist是非執行緒安全,假設10個執行緒同時執行,往Arraylist新增100條資料,有可能出現Arraylist最終的資料總和會小於1000,所以開發中要注意,可以通過鎖去解決。而Vector就是執行緒安全的,不過Vector也有自身的缺點,如果儲存大量的資料,Vector會消耗的資源較大,或者做插入、刪除、resize等操作都有可能導致Vector的迭代器失效,總之各有優點。
Arraylist內部,就先從構造方法看起:
//內部儲存資料,數量變化到一定程度去操作的陣列 transient Object[] elementData; //資料個數 private int size; //預設初始化容量為10 private static final int DEFAULT_CAPACITY = 10; //預設空陣列 private static final Object[] EMPTY_ELEMENTDATA = {}; public ArrayList(int initialCapacity) { if (initialCapacity > 0) { //指定大小的初始化 this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { //使用預設空陣列 this.elementData = EMPTY_ELEMENTDATA; } else { //容量大小 < 0時候丟擲的異常 throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } } public ArrayList() { //無參建構函式,初始化一個空的陣列 this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
當我們在初始化Arraylist時,如未指定大小,儲存資料的陣列會設定預設大小為10。後面當儲存資料個數大於10時,Arraylist的儲存陣列會指向新陣列,且新陣列的大小為舊陣列大小的1.5倍。後面大小如接著超過,會重複這個操作,想想資料量大的時候,使用Arraylist確實會影響效能。下面看看擴容的方法:
private void grow(int minCapacity) { // 舊陣列數量和擴容後的數量 int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); //新容量比期望的最小容量小,將期望的最小容量作為當前容量 if (newCapacity - minCapacity < 0) newCapacity = minCapacity; //新容量大於規定最大值,呼叫大的“擴容”方法hugeCapacity if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); //舊的數量容量變大,原資料存入,索引不變 elementData = Arrays.copyOf(elementData, newCapacity); } private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) throw new OutOfMemoryError(); //最小期望大小如大於MAX_ARRAY_SIZE,設定新容量為(2^31-1) return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
因為內部是陣列,所以保留了索引的概念,因為add和remove和get等方法能很好理解,主要就是add函式多次呼叫到一定數量會觸發"擴容"。
2.LinkedList
LinkedList是一種連結串列的結構,看看其內部的一個私有類Node,正式這些內部的node節點類組成了連結串列的形式
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
/**
* Pointer to first node.
* 指向第一個node
*/
transient Node<E> first;
/**
* Pointer to last node.
* 指向最後一個lode
*/
transient Node<E> last;
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;
}
}
......
}
單看一個node類節點。item變數就是本節點值,prev就是前一個節點值,next即為下一個節點值。這樣就形成了一條節點關聯起來的鏈式結構。
LinkedList有一些特殊的函式,比如:
public void addFirst(E e) {
linkFirst(e);
}
public void addLast(E e) {
linkLast(e);
}
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
public E removeLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
看名字估計也能知道,就是直接在連結串列頭、尾新增或者刪除資料。
3.Vector
Vector為向量佇列,內部儲存資料也是陣列,採用的儲存方式類似於LinkedList的"擴容",先看看它的4個構造方法:
//capacity是Vector的預設容量大小,capacityIncrement是每次Vector容量增加時的增量值。
public Vector(int initialCapacity, int capacityIncrement) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
}
//指定Vector的容量大小
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
//構造一個空的向量,內部資料陣列大小為10
public Vector() {
this(10);
}
//構造一個包含指定元素元素的向量集合,按照集合的順序返回
public Vector(Collection<? extends E> c) {
elementData = c.toArray();
elementCount = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, elementCount, Object[].class);
}
其空參構造方法,預設建立容量為10的陣列。同時也可以指定大小,主要看看capacityIncrement這個引數,解釋為"擴容增量",可以先看看下面的程式碼:
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//擴容增量的設定與否影響擴容後的大小
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
可以看到如無設定capacityIncrement,容器數量個數會增長為當前容器數量的一倍。
內部常見函式有:
轉載這個老哥的方法整理,有點多:https://www.cnblogs.com/zedosu/p/6662231.html
synchronized boolean add(E object)
void add(int location, E object)
synchronized boolean addAll(Collection<? extends E> collection)
synchronized boolean addAll(int location, Collection<? extends E> collection)
synchronized void addElement(E object)
synchronized int capacity()
void clear()
synchronized Object clone()
boolean contains(Object object)
synchronized boolean containsAll(Collection<?> collection)
synchronized void copyInto(Object[] elements)
synchronized E elementAt(int location)
Enumeration<E> elements()
synchronized void ensureCapacity(int minimumCapacity)
synchronized boolean equals(Object object)
synchronized E firstElement()
E get(int location)
synchronized int hashCode()
synchronized int indexOf(Object object, int location)
int indexOf(Object object)
synchronized void insertElementAt(E object, int location)
synchronized boolean isEmpty()
synchronized E lastElement()
synchronized int lastIndexOf(Object object, int location)
synchronized int lastIndexOf(Object object)
synchronized E remove(int location)
boolean remove(Object object)
synchronized boolean removeAll(Collection<?> collection)
synchronized void removeAllElements()
synchronized boolean removeElement(Object object)
synchronized void removeElementAt(int location)
synchronized boolean retainAll(Collection<?> collection)
synchronized E set(int location, E object)
synchronized void setElementAt(E object, int location)
synchronized void setSize(int length)
synchronized int size()
synchronized List<E> subList(int start, int end)
synchronized <T> T[] toArray(T[] contents)
synchronized Object[] toArray()
synchronized String toString()
synchronized void trimToSize()
4.Stack
稱之為棧,本身繼承自Vector,有著資料先進後出的特性。
內部儲存也是由陣列實現
protected Object[] elementData;
常用的幾個方法:
//push:將元素新增到陣列的末尾
public E push(E item) {
addElement(item);
return item;
}
//加了鎖,可以看出push新增資料也執行緒安全的
public synchronized void addElement(E obj) {
//數量+1,下個索引資料指向引數傳遞的資料
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = obj;
}
//peek:取出棧頂元素,不執行刪除
public synchronized E peek() {
int len = size();
if (len == 0)
throw new EmptyStackException();
//返回指定索引資料
return elementAt(len - 1);
}
//pop時:取出棧頂元素,並將該元素從棧中刪除
public synchronized E pop() {
E obj;
int len = size();
//拿到最後一個數據
obj = peek();
//刪除最後一個數據
removeElementAt(len - 1);
return obj;
}
5.hashmap
hashmap內部是16位的陣列,陣列中儲存的每一個元素又是連結串列的表頭,表頭這個節點是個Entry型別。
final K key;
V value;
final int hash;
HashMapEntry<K, V> next;
上圖看出Entry中包含鍵對值中的key和value,next指向的是下一個節點Entry。hash作用主要用來計算值儲存的位置,公式為:
hash(key)%len。將hash(key)值對len,也就是16取餘。