List 與Set接口
Collection 接口存儲一組不唯一、無序的對象
List接口存儲一組不唯一、有序(插入順序)的對象
Set接口存儲一組唯一、無序的對象
Map接口存儲一組鍵值對象,提供key到value的映射
set接口中的實現類
HashSet:採用Hashtable哈希表存儲結構
優點:添加速度快,查詢速度快,刪除速度快
缺點:無序
TreeSet
採用二叉樹(紅黑樹)的存儲結構
優點:有序(排序後的升序)查詢速度比List快
缺點:查詢速度沒有HashSet快
java 容器 collection 和map的區別。collection中存儲了一組對象,而map存儲的是key、value,並且key不能重複。
Map:
HashMap:非線程安全、無序、key不重複。
TreeMap:非線程安全、有序、key不重複。
Hashtable:線程安全、無序、不重複。
ConcurrentHashMap:線程安全,有序、不重複。
Collection
set: 有序、不重複對象
TreeSet:非線程安全、有序、不重複。
HashSet:非線程安全、有序、不重複。
List: 按插入順序、對象可重複
TreeList:非線程安全、按插入順序、可重複。
ArrayList:非線程安全、插入順序、可重複。
LinkedList:非線程安全、插入順序、可重複。
Vector:線程安全、插入順序、可重複。
Arraylist : 初始大小爲10,原大小+ 原大小右移1位 = 1.5 擴容。oldCapacity + (oldCapacity >> 1)。插入刪除時都會copy一份。
Vector: 直接2倍擴容。oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity)
HashMap: 初始16 ,2倍(2的n次冪)擴容.左移1位.newCap = oldCap << 1
爲什麼hashmap容量是2的n次冪?
1.方便進行 與 運算 (HashMap: 376行),與運算比取模運算效率高
2.在擴容之後涉及到元素的遷移過程,遷移的時候只需要判斷二進制的前一位是0或者1即可。
如果是0,表示新數組和舊數組的下標位置不變,如果是1,只需要將索引位置加上舊的數組的長度值即爲新數組的下標
ArrayList 源碼分析
add(Object o )
/**
* 將當前數組大小+1傳入 ensureCapacityInternal方法
* 將e 對象添加到 elementData 數組的 當前數組+1位置。數據的最後一位,非數組的末尾!!!
* 並且每次添加對象,數組的大小都會1.5倍擴張
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 當前數組大小 + 1
elementData[size++] = e;
return true;
}
/**
* 判斷下 elementData 是否是空的,空的就取最小值10
* 如果當前數組+1 的大小大於10,就取當前數組+1
* @param minCapacity
*/
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { // 如果這個數組是空的
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); //初始化一個 10 的大小,或者數組+1 個
}
ensureExplicitCapacity(minCapacity);
}
public static int max(int a, int b) {//a=10 ,b=當前數組大小
return (a >= b) ? a : b;
}
/**
* 如果當前數組+1 大於 elementData的length 執行grow
* @param minCapacity
*/
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0) //一般是true。當前數組+1 大於 當前數組大小
grow(minCapacity);
}
/**
* 先獲取 elementData 的length,然後*1.5倍。如果這個值比當前數組+1 小,就取當前數組+1 minCapacity
* 如果 newCapacity 比 最大值大,就取 最大值或者 最大值減8
* 將 elementData 數組擴大至 當前數組的 1.5 倍
*/
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length; //當前數據的大小
int newCapacity = oldCapacity + (oldCapacity >> 1); // 1.5 倍。右移運算,不懂的可以for循環打出,數組新值
if (newCapacity - minCapacity < 0) //如果 新值 小於當前數組+1
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);
}
/**
* 當前數組+1 大於最大值 就返回 integer最大值,否則返回integer最大值-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;
}
通過源碼可以看到,arraylist 添加對象的時候,初始大小是10,然後會進行每次1.5倍的擴張。
而且添加的時候,是將現有的數組進行copy一份的。所以插入效率不高。
remove(object o)
/**
* 先進行遍歷,獲取到要刪除的位置的索引。
* 然後調用系統的刪除方法
*/
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
/**
* 進行copy刪除
*/
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
}
可以發現,remove效率也不高,先遍歷再copy一份刪除。
LinkedList
/**
* 先要取出next,然後再取出next的item進行比較
*/
public int indexOf(Object o) {
int index = 0;
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null)
return index;
index++;
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item))
return index;
index++;
}
}
return -1;
}
/**
* 直接鏈到最後面
*/
public boolean add(E e) {
linkLast(e);
return true;
}
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++;
}
插入效率高、刪除速度快。遍歷和隨機訪問效率低。
爲什麼說linkedlist是雙向鏈表?
因爲在linkedlist中存儲的Node,會記錄上一個下一個node
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;
}
}
Vector
add: 添加時進行當前大小的*2擴增,並且是線程安全的。
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
/**
* newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity)
* 兩倍擴增
*/
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);
}
Queue 和 list的區別
Queue 和list 的區別。queue 添加了很多對線程友好的api : offer、peek、poll。 put、take 會阻塞
Queue :當隊列滿了的時候調用 add方法會拋出異常,同理remove也會拋出異常。
queue提供了offer、peek、poll的支持。
offer 添加,返回值boolean,不會報錯
peek 返回隊列的第一個(頭)元素,不會刪除
poll 返回隊列頭元素並刪除
ArrayBlockingQueue 實現了 BlockingQueue, BlockingQueue 繼承自Queue
ArrayBlockingQueue 有take方法,得到頭元素並刪除,如果queue是空的就進行等待(阻塞),需要捕獲異常
ArrayBlockingQueue 有put方法,添加時如果滿了就進行等待(阻塞),需要捕獲異常。
ArrayBlockingQueue 是線程安全的,方法裏面加入了 ReentrantLock
Arrays
用於對數組操作的工具類
java.util.Collections 類!! 而不是接口 Collection
這個類是專門對實現了List的類提供了一些靜態方法支持,以及提供了將非同步Set、List、Map轉換成同步的方法
sort()排序: Collections.sort(list);
public static <T extends Comparable<? super T>> void sort(List<T> list) {
list.sort(null);
}
如果是自定義的對象,需要實現 Comparable 接口
binarySearch : 對半查找
//很明顯會調用 iteratorBinarySearch
public static <T>
int binarySearch(List<? extends Comparable<? super T>> list, T key) {
if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
return Collections.indexedBinarySearch(list, key);
else
return Collections.iteratorBinarySearch(list, key);
}
/**
* 這裏使用了一個無符號右移(邏輯右移) int mid = (low + high) >>> 1;
* 上面一行的 運算結果 爲((low + high)) 的一半
* 所以這個查找也叫對半查找
*/
private static <T>
int iteratorBinarySearch(List<? extends Comparable<? super T>> list, T key)
{
int low = 0;
int high = list.size()-1;
ListIterator<? extends Comparable<? super T>> i = list.listIterator();
while (low <= high) {
int mid = (low + high) >>> 1;
Comparable<? super T> midVal = get(i, mid);
int cmp = midVal.compareTo(key);
if (cmp < 0)
low = mid + 1;
else if (cmp > 0)
high = mid - 1;
else
return mid; // key found
}
return -(low + 1); // key not found
}
//遍歷查找
private static <T> T get(ListIterator<? extends T> i, int index) {
T obj = null;
int pos = i.nextIndex();
if (pos <= index) {
do {
obj = i.next();
} while (pos++ < index);
} else {
do {
obj = i.previous();
} while (--pos > index);
}
return obj;
}
shuffle:隨機排序
public static void shuffle(List<?> list) {
Random rnd = r;
if (rnd == null)
r = rnd = new Random(); // harmless race.
shuffle(list, rnd);
}
reverse:倒序
public static void reverse(List<?> list) {
int size = list.size();
if (size < REVERSE_THRESHOLD || list instanceof RandomAccess) {
for (int i=0, mid=size>>1, j=size-1; i<mid; i++, j--)
swap(list, i, j);
} else {
// instead of using a raw type here, it's possible to capture
// the wildcard but it will require a call to a supplementary
// private method
ListIterator fwd = list.listIterator();
ListIterator rev = list.listIterator(size);
for (int i=0, mid=list.size()>>1; i<mid; i++) {
Object tmp = fwd.next();
fwd.set(rev.previous());
rev.set(tmp);
}
}
}
copy:從一個集合複製到另一個集合
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
int srcSize = src.size();
if (srcSize > dest.size())
throw new IndexOutOfBoundsException("Source does not fit in dest");
if (srcSize < COPY_THRESHOLD ||
(src instanceof RandomAccess && dest instanceof RandomAccess)) {
for (int i=0; i<srcSize; i++)
dest.set(i, src.get(i));
} else {
ListIterator<? super T> di=dest.listIterator();
ListIterator<? extends T> si=src.listIterator();
for (int i=0; i<srcSize; i++) {
di.next();
di.set(si.next());
}
}
}