List接口——Collection子接口
1. List接口
1)概述
- 存儲一個一個的數據
- 有序
2)常用的方法
List是Collection的子接口,Collection中聲明的方法,在List的實現類中都可以使用。由於List是有序的,所有額外添加了一些方法。
- void add(int index, Object ele): 在index位置插入ele元素
- boolean addAll(int index, Collection eles): 從index位置開始將eles中的所有元素添加進來
- Object get(int index): 獲取指定index位置的元素
- int indexOf(Object obj): 返回obj在集合中首次出現的位置
- int lastIndexOf(Object obj): 返回obj在當前集合中末次出現的位置
- Object remove(int index): 移除指定index位置的元素,並返回此元素
- Object set(int index, Object ele): 設置指定index位置的元素爲ele
- List subList(int fromIndex, int toIndex): 返回從fromIndex到toIndex位置的子集合
3)總結常用方法(必須掌握的)
- 增: add(Object obj)
- 刪: remove(Object obj)/remove(int index)
- 改: set(int index, Object ele)
- 查: get(int index)
- 插: add(int index, Object ele)
- 長度: size()
- 遍歷: iterator() / 增強for
4)代碼舉例
List list = new ArrayList();
list.add(123);//自動裝箱
list.add(567);
list.add("AA");
list.remove("AA");
list.remove(new Integer(123));
list.set(1, "BB");
list.add(1, "CC");
System.out.println(list);
System.out.println(list.get(2));
System.out.println(list.size());
List遍歷
@Test
public void test2(){
List list = new ArrayList();
list.add(123);//自動裝箱
list.add(567);
list.add("AA");
//方式一:使用迭代器
Iterator iterator = list.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
//方式二:增強for
// for(Object obj : list){
// System.out.println(obj);
// }
//方式三:一般for循環
// for(int i = 0;i < list.size();i++){
// System.out.println(list.get(i));
// }
}
@Test
public void test3(){
List list = new ArrayList();
list.add(123);//自動裝箱
list.add(567);
list.add("AA");
List list1 = Arrays.asList(1,2,3);//數組轉換爲List
list.addAll(1, list1);
System.out.println(list);
List list2 = list.subList(1,3);//List截取,左閉右開
System.out.println(list2);
}
2. List不同的實現類
1) ArrayList(最常用的)
- List的主要實現類;
- 線程不安全的,效率高;
- 底層使用Object[]存儲
2) LinkedList
- 底層使用雙向鏈表存儲數據;
- 對於頻繁的插入、刪除操作,使用此類效率高。
3) Vector
List的古老實現類;
線程安全的,效率低;
底層使用Object[]存儲
4) 擴展:數據結構中的數據存儲結構
- 真實數據存儲結構:
順序表(一維數組)、鏈表 - 抽象數據存儲結構
棧、隊列、樹、圖
3. List的源碼分析
1) jdk7版本源碼
- 構造器:初始化底層的elementDate的Object[]數組,長度爲10.
ArrayList list = new ArrayList();
transient Object[] elementData; // non-private to simplify nested class access//list底層的對象數組
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this(10);
}
/**
* Constructs an empty list with the specified initial capacity.
*
* @param initialCapacity the initial capacity of the list
* @throws IllegalArgumentException if the specified initial capacity
* is negative
*/
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
}
- 首次add():elementDate[0] = new Integer(123);
list.add(123);
當遇到底層數組容量達到零界點時,調用add(“AA”),一旦添加的數據的個數超出了底層數組的長度,需要考慮擴容。
默認容量擴容擴容爲原來的1.5倍,同時將舊數組中的數據都複製到新的數組中。
/**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*/
private int size;//list中包含的元素的個數
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!//確認是否需要擴容
elementData[size++] = e;//執行到這裏說明elementData的容量夠用。
return true;
}
確認是否需要擴容
private void ensureCapacityInternal(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
//如果minCapacity大於elementData.length,說明此此添加list的容量已經不夠,需要擴容
grow(minCapacity); //擴容
}
擴容
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);//默認擴容比例:1.5倍
//特殊情況的擴容比例:
if (newCapacity - minCapacity < 0)
//如果擴容1.5倍後依然不夠,則直接擴容至需要的容量
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
//如果擴容至需要的容量後,已經超過數組的最大限制
newCapacity = hugeCapacity(minCapacity);
//創建新長度的數組,並將原有數組中的數據copy到新的數組中。
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
2)jdk8版本源碼分析
- 構造器:初始化底層elementData的Object[]數組爲{}
ArrayList list = new ArrayList();
//懶加載:
//使用空參構造器,初始化爲長度爲0的object數組。
//當有任何操作後,長度更改爲DEFAULT_CAPACITY=10。
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added.
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
list.add(123);
- 首次add(): 此時底層創建長度爲10的elementData數組,並將new Integer(123)存放到角標0的索引位置。
- 當底層數組容量達到零界點時,此時調用add(“AA”),一旦添加的數據的個數超出了底層數組的長度,就需要考慮擴容 。
list.add("AA");
默認容量擴容擴容爲原來的1.5倍,同時將舊數組中的數據都複製到新的數組中。
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
初始化底層數組
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;//默認初始化list容量
private void ensureCapacityInternal(int minCapacity) {
//如果是首次調用add(),則此時的minCapacity賦值爲10
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {//==,說明是第一次添加數據
//第一次添加數據,需要初始化list容量,取默認值和第一次添加需要容量的最大值,也就是10
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
//判斷是否需要擴容
ensureExplicitCapacity(minCapacity);
}
判斷是否需要擴容
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
//首次添加數據,minCapacity(10)大於實際數組長度elementData.length(0)
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
擴容:擴容爲原來的1.5倍
/**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);//默認擴容爲原來的1.5倍
//特殊情況下的擴容方案:
if (newCapacity - minCapacity < 0)
//擴容後依然不夠需要的容量,就直接擴容至需要的容量
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
//擴容後的容量超過了最大容量限制,就設置爲int型最大值或最大容量
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
3)說明
jdk7中ArrayList底層的數組的創建類似於單例模式中的餓漢式
jdk8中ArrayList底層的數組的創建類似於單例模式中的懶漢式
6. Vector源碼分析
1) 構造器
Vector v = new Vector();
public Vector() {
this(10);//默認初始容量爲10
}
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
public Vector(int initialCapacity, int capacityIncrement) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
}
/**
* The amount by which the capacity of the vector is automatically
* incremented when its size becomes greater than its capacity. If
* the capacity increment is less than or equal to zero, the capacity
* of the vector is doubled each time it needs to grow.
*
* @serial
*/
//需要擴容時,Vector自動擴容的量。
//如果擴容增量小於或等於0,則每次需要增長時,向量的容量將增加一倍。
protected int capacityIncrement;
2)add()
/**
* Appends the specified element to the end of this Vector.
*
* @param e element to be appended to this Vector
* @return {@code true} (as specified by {@link Collection#add})
* @since 1.2
*/
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
判斷是否需要擴容
/**
* This implements the unsynchronized semantics of ensureCapacity.
* Synchronized methods in this class can internally call this
* method for ensuring capacity without incurring the cost of an
* extra synchronization.
*
* @see #ensureCapacity(int)
*/
private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
擴容
- jdk7和jdk8中在創建對象時,底層的操作相同,都是創建默認長度爲10的Object[]。
- 當底層容量不足時,默認擴容爲原來的2倍,然後將之前的數組元素,複製到新的數組中
- 也可以在創建時,在構造器中顯式指明擴容是自動擴容的增量。
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);
}
7. LinkedList源碼分析
1)構造器
LinkedList list = new LinkedList();
/**
* Constructs an empty list.
*/
public LinkedList() {
}
2)鏈表節點內部類
- LinkedList在添加數據時,元素封裝在Node對象中,並指明其前一個和後一個元素。
- LinkedList不存在擴容的問題。因爲底層不是使用的數組,在內存中多個元素也不是連續存放的。
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;//前向指針的產生
}
}
transient Node<E> first;
transient Node<E> last;
2) add()
/**
* Appends the specified element to the end of this list.
*
* <p>This method is equivalent to {@link #addLast}.
*
* @param e element to be appended to this list
* @return {@code true} (as specified by {@link Collection#add})
*/
public boolean add(E e) {
linkLast(e);
return true;
}
/**
* Links e as last element.
*/
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)//返回true,表示是首次添加
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
3) addFirst()/addLast()
添加元素到鏈表的第一個/最後一個位置。
public void addFirst(E e) {
linkFirst(e);
}
/**
* Links e as first element.
*/
private void linkFirst(E e) {
final Node<E> f = first;
final Node<E> newNode = new Node<>(null, e, f);
first = newNode;
if (f == null)
last = newNode;
else
f.prev = newNode;
size++;
modCount++;
}
public void addLast(E e) {
linkLast(e);
}
/**
* Links e as last element.
*/
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
4) getFirst()/getLast()
獲取鏈表第一個/最後一個位置元素。
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
public E getLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return l.item;
}
5) removeFirst()/removeLast()
刪除並返回第一個/最後一個元素。如果沒有該元素,則拋出異常。
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);
}
6) pollFirst()/pollLast()
刪除並返回第一個/最後一個元素。如果沒有該元素,則返回null。
public E pollFirst() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
private E unlinkFirst(Node<E> f) {
// assert f == first && f != null;
final E element = f.item;
final Node<E> next = f.next;
f.item = null;
f.next = null; // help GC
first = next;
if (next == null)
last = null;
else
next.prev = null;
size--;
modCount++;
return element;
}
public E pollLast() {
final Node<E> l = last;
return (l == null) ? null : unlinkLast(l);
}
private E unlinkLast(Node<E> l) {
// assert l == last && l != null;
final E element = l.item;
final Node<E> prev = l.prev;
l.item = null;
l.prev = null; // help GC
last = prev;
if (prev == null)
first = null;
else
prev.next = null;
size--;
modCount++;
return element;
}
8. 小結
1) 建議開發中,如果基本確定底層數組的容量,建議使用帶參數的構造器,避免底層不斷的擴容和複製操作
ArrayList list = new ArrayList(int initialCapacity);//new Object[initialCapacity];
2) 對於數組來說,查找操作的複雜度是O(1),插入或刪除操作的複雜度是O(n)
對於鏈表來說,查找操作的複雜度是O(n),插入或刪除操作的複雜度是O(1)
3) 開發中,如果很少執行插入或刪除操作,建議使用ArrayList
如果頻繁的使用插入或刪除操作,建議使用LinkedList