1.集合類
數組:可以存儲對象,也可以存儲基本數據類型,但是一次只能存儲一種類型,且長度一定,不可改變。
集合:只能存儲對象,長度可變,可以存儲不同類型的對象。Java集合類主要有三種:set,list,map
其中,實現邊框的是實現類,折線邊框的是抽象類,點線邊框的是接口
從圖中可以看出,Collection接口是集合類(List,Set,Queue)的根接口,java中沒有提供這個接口的直接實現類。有三個子接口List,Set,Queue,注意,Map不是collection的子接口。
Collection中的方法:
2.Collection中的List和Set接口
首先說一下List接口。 List里存放的對象是有序的,可重復的,可以為null的集合。List關注的是索引,擁有一系列和索引相關的方法,查詢速度快。
List接口下主要的三個實現類:Arraylist,Linkedlist,Vector。
(1)ArrayList
arraylist實現List接口,繼承AbstractList。底層是數組實現,可以自增擴容。是非線程安全的,一般用於單線程環境中(與Vector最大的區別就是,V是線程安全的,所以A比V的性能相對要好些),在多線程中,可以選擇Vector或者CopyOnWriteArrayList。Arraylist實現了Serializable接口,支持序列化,能夠通過序列化傳輸;實現了RandomAccess接口(只是個標注接口,沒有實際的方法),支持快速隨機訪問,主要變現為可以通過下標直接訪問(因為Arraylist的底層是數組,可直接用數組下標來索引);實現了Cloneable接口,能被克隆。 Arraylist是基於動態數組實現的。
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable
1)初始化
Arraylist提供了三種初始化方法。
public ArrayList(int initialCapacity) { if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { this.elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } } /** * Constructs an empty list with an initial capacity of ten.//默認提供容量為10的數組。 */ public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } /** * Constructs a list containing the elements of the specified * collection, in the order they are returned by the collection's * iterator. * * @param c the collection whose elements are to be placed into this list * @throws NullPointerException if the specified collection is null */ public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); if ((size = elementData.length) != 0) { // c.toArray might (incorrectly) not return Object[] (see 6260652) if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); //注意size是記錄該list集合當前元素的數量,不是容量 } else { // replace with empty array. this.elementData = EMPTY_ELEMENTDATA; } }
2)動態調整
無參構造函數默認的是空數組,為什么注釋說是容量為10的數組。主要是ArrayList的add方法。add方法中調用了ensureCapacityInternal()方法,
/** * 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; } private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } private static int calculateCapacity(Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; }
從上述源碼中可以看出,當elementData為空數組時,則使用Math.max(DEFAULT_CAPACITY, minCapacity)進行選擇一個最大的,其中DEFAULT_CAPACITY為arraylist定義的靜態常量=10;
private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); } /** * 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); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: 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; }
動態擴容最關鍵是grow方法 。通過源碼中int newCapacity = oldCapacity + (oldCapacity >> 1);可得容量擴大為原來的1.5倍。
總之,ArrayList默認容量是10,如果初始化時一開始指定了容量,或者通過集合作為元素,則容量為指定的大小或參數集合的大小。每次擴容為原來的1.5倍,如果新增后超過這個容量,則容量為新增后所需的最小容量。如果增加1.5倍后的新容量超過限制的容量,則用所需的最小容量與限制的容量進行判斷,超過則指定為Integer的最大值,否則指定為限制容量大小。然后通過數組的復制將原數據復制到一個更大(新的容量大小)的數組。
3)遍歷方式
第一,隨機訪問,通過索引獲取元素。ArrayList實現了randomaccess接口。
/** * Returns the element at the specified position in this list. * * @param index index of the element to return * @return the element at the specified position in this list * @throws IndexOutOfBoundsException {@inheritDoc} */ public E get(int index) { rangeCheck(index); return elementData(index); }
第二,for循環,foreach循環。
package Two; import java.util.ArrayList; public class one { public static void main(String[] args) { ArrayList<Integer> arrayList = new ArrayList(); arrayList.add(1); arrayList.add(3); arrayList.add(9); //for循環 for (int i = 0;i<arrayList.size();i++){ System.out.print(arrayList.get(i)); } // foreach循環 for (Integer list:arrayList) { System.out.print(list); } } }
第三種:通過迭代器遍歷
public static void main(String[] args) { ArrayList<Integer> arrayList = new ArrayList(); arrayList.add(1); arrayList.add(3); arrayList.add(9); Integer integer = null; Iterator iterator = arrayList.iterator(); while (iterator.hasNext()){ integer = (Integer) iterator.next(); System.out.println(integer); } }
上述三種遍歷方式中,隨機訪問的效率最高,使用迭代器的效率最低。
總結:
-
ArrayList是List接口的一個可變大小的數組的實現
-
ArrayList的內部是使用一個Object對象數組來存儲元素的
-
初始化ArrayList的時候,可以指定初始化容量的大小,如果不指定,就會使用默認大小,為10
-
當添加一個新元素的時候,首先會檢查容量是否足夠添加這個元素,如果夠就直接添加,如果不夠就進行擴容,擴容為原數組容量的1.5倍
-
當刪除一個元素的時候,會將數組右邊的元素全部左移,添加一個元素時,右移。
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, Serializable
請注意,此實現不同步。 如果多個線程同時訪問鏈接列表,並且至少有一個線程在結構上修改列表,則必須在外部進行同步。 (結構修改是添加或刪除一個或多個元素的任何操作;僅設置元素的值不是結構修改。)這通常通過在自然封裝列表的對象上進行同步來實現。 如果沒有這樣的對象存在,列表應該使用Collections.synchronizedList方法“包裝”。 這最好在創建時完成,以防止意外的不同步訪問列表: List list = Collections.synchronizedList(new LinkedList(...));
源碼閱讀有興趣可自己去看
LinkedList特點:
- 雙向鏈表實現,沒有固定容量,不需擴容
- 元素是有序的,允許null值,輸入輸出順序一致
- 所有指定位置的操作都是從頭開始遍歷的
- 需要更多的內存,LinkedList每個節點中需要存儲前后節點的信息,占用空間更多
- 查找效率低,插入刪除效率高。
3)Vector
Vector非常類似ArrayList,但是Vector是同步的。由Vector創建的Iterator,雖然和ArrayList創建的Iterator是同一接口,但是,因為Vector是同步的,當一個Iterator被創建而且正在被使用,另一個線程改變了Vector的狀態(例如,添加或刪除了一些元素),這時調用Iterator的方法時將拋出ConcurrentModificationException,因此必須捕獲該異常。
vector源碼分析:https://www.cnblogs.com/skywang12345/p/3308833.html
3.List實現類的各種比較
- ArrayList是實現了基於動態數組的數據結構,而LinkedList是基於雙向鏈表的數據結構
- 對於隨機訪問,ArrayList要優於LinkedList,因為LinkedList要移動指針
- 對於插入和刪除,LinkedList較占優勢,ArrayList要移動數據。
- ArrayList和LinkedList都是非線程安全的容器
在實際使用中,若對數據的主要操作為索引或只在集合的末端增加、刪除元素,使用Arraylist和vector效率比較高;若對數據的操作主要為指定位置的插入或刪除操作,使用Linkedlist效率比較高;當在多線程中使用容器時(即多個線程會同時訪問該容器,),選用vector較為安全。