一、前言
整個集合框架的常用類我們已經分析完成了,但是還有兩個工具類我們還沒有進行分析。可以說,這兩個工具類對於我們操作集合時相當有用,下面進行分析。
二、Collections原始碼分析
2.1 類的屬性
public class Collections { // 二分查詢閾值 private static final int BINARYSEARCH_THRESHOLD = 5000; // 反向閾值 private static final int REVERSE_THRESHOLD = 18; // 洗牌閾值 private static final int SHUFFLE_THRESHOLD = 5; // 填充閾值 private static final int FILL_THRESHOLD = 25; // 旋轉閾值 private static final int ROTATE_THRESHOLD = 100; // 拷貝閾值 private static final int COPY_THRESHOLD = 10; // 替換閾值 private static final int REPLACEALL_THRESHOLD = 11; // 子集合索引閾值 private static final int INDEXOFSUBLIST_THRESHOLD = 35; }
2.2 建構函式
private Collections() { }
說明:私有建構函式,在類外無法呼叫。
2.3 方法分析
下面是Collections的所有方法。
可以看到,Collections的方法包含了各種各樣的操作。下面分析最常用的方法。
1. sort函式
該函式有兩個過載函式,方法簽名分別如下
public static <T extends Comparable<? super T>> void sort(List<T> list) public static <T> void sort(List<T> list, Comparator<? super T> c)
說明:對於第一個函式,引數為List<T> list,表示只能對List進行排序。由<T extends Comparable<? super T>>可知,T型別或者是T的父型別必須實現了Comparable介面。對於第二個函式,也包含了List<T> list引數,還包含了Comparator<? super T> c,表示可以指定自定義比較器Comparator。而元素型別不需要實現Comparable介面。兩個函式都會呼叫到List類的sort方法。具體如下
default void sort(Comparator<? super E> c) { // 轉化為陣列 Object[] a = this.toArray(); // 呼叫Arrays類的sort函式 Arrays.sort(a, (Comparator) c); // 迭代器 ListIterator<E> i = this.listIterator(); for (Object e : a) { i.next(); i.set((E) e); } }
2. binarySearch函式
該函式也有兩個過載版本,方法簽名分別如下
public static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key) public static <T> int binarySearch(List<? extends T> list, T key, Comparator<? super T> c)
說明:與sort函式的兩個過載版本類似,可以指定自定義比較器,特別注意,使用此函式時,必須要保證List已經排好序,並且集合元素是可以比較的。其中,兩個binarySearch函式會呼叫indexedBinarySearch函式,具體函式如下
private static <T> int indexedBinarySearch(List<? extends Comparable<? super T>> list, T key) { int low = 0; int high = list.size()-1; while (low <= high) { // 取low - high 的中間索引,直接使用移位操作,效率更高 int mid = (low + high) >>> 1; // 取中間元素 Comparable<? super T> midVal = list.get(mid); // 中間元素與key比較 int cmp = midVal.compareTo(key); if (cmp < 0) // 小於key,在高半部分查詢 low = mid + 1; else if (cmp > 0) // 大於key,在低半部分查詢 high = mid - 1; else // 找到key,返回位置 return mid; // key found } return -(low + 1); // 沒有找到key,返回負數 }
說明:該函式在List可以隨機訪問時被呼叫,效率相比iteratorBinarySearch更高,當List不能被隨機訪問時,將採用iteratorBinarySearch進行二分查詢,即採用迭代器模式。iteratorBinarySearch具體程式碼如下
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); // 中間元素與key比較 int cmp = midVal.compareTo(key); if (cmp < 0) // 小於key,在高半部分查詢 low = mid + 1; else if (cmp > 0) // 大於key,在低半部分查詢 high = mid - 1; else // 找到key,返回位置 return mid; } return -(low + 1); // 沒有找到key,返回負數 }
說明:該函式會呼叫get函式,即遍歷集合找元素,所以效率相對較低。get函式如下
private static <T> T get(ListIterator<? extends T> i, int index) { T obj = null; // 下一個結點索引 int pos = i.nextIndex(); if (pos <= index) { //下一個結點索引小於index,從前往後 do { obj = i.next(); } while (pos++ < index); } else { // 下一個結點索引不小於index,從後往前 do { obj = i.previous(); } while (--pos > index); } // 返回元素 return obj; }
3. reverse函式
此函式用於反轉集合中的元素,其簽名如下
public static void reverse(List<?> list)
具體程式碼如下
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 { // 否則 // 獲取從頭開始的迭代器 ListIterator fwd = list.listIterator(); // 獲取從size位置(從尾)開始的迭代器 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); } } }
說明:若集合支援隨機訪問或者集合大小小於反轉閾值,則採用直接交換操作;否則,就會採用雙迭代器模式(從頭開始的,從尾開始的)進行交換。
4. fill函式
此函式用於給集合填充指定元素,簽名如下
public static <T> void fill(List<? super T> list, T obj)
泛型方法,具體程式碼如下
public static <T> void fill(List<? super T> list, T obj) { int size = list.size(); if (size < FILL_THRESHOLD || list instanceof RandomAccess) { // 小於填充閾值或者集合可以隨機訪問 // 遍歷集合 for (int i=0; i<size; i++) list.set(i, obj); } else { // 否則 // 使用迭代器模式進行填充 ListIterator<? super T> itr = list.listIterator(); for (int i=0; i<size; i++) { itr.next(); itr.set(obj); } } }
說明:也是同reverse函式一樣,分為兩種情況處理。
5. copy函式
此函式用於拷貝集合,將源集合拷貝至目標集合,簽名如下
public static <T> void copy(List<? super T> dest, List<? extends T> src)
其具體程式碼如下
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)) { // 小於拷貝閾值或者(src和dest集合都支援隨機訪問 // 遍歷,拷貝 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()); } } }
說明:也是分為兩種情況進行處理,並且要確保目標集合大小大於源集合大小。
6. min函式
此函式用於求得集合裡最小的元素,有兩個過載版本,簽名如下
public static <T extends Object & Comparable<? super T>> T min(Collection<? extends T> coll) public static <T> T min(Collection<? extends T> coll, Comparator<? super T> comp)
說明:可以指定比較器,取出自定義的最小的元素。第一個函式具體程式碼如下
public static <T extends Object & Comparable<? super T>> T min(Collection<? extends T> coll) { // 獲取迭代器 Iterator<? extends T> i = coll.iterator(); // 第一個元素為候選元素 T candidate = i.next(); while (i.hasNext()) { // 下一個元素 T next = i.next(); // 下一個元素小於候選元素 if (next.compareTo(candidate) < 0) // 改變候選元素 candidate = next; } // 返回最小元素 return candidate; }
說明:只需要遍歷一遍集合即可取出最小值,另外一個過載函式類似,不再累贅,max函式與min函式類似,不再累贅。
7. rotate函式
此函式用於旋轉集合元素,實際就是迴圈右移集合裡的元素,簽名如下
public static void rotate(List<?> list, int distance)
集合元素迴圈右移,移動的距離為distance,具體程式碼如下
public static void rotate(List<?> list, int distance) { if (list instanceof RandomAccess || list.size() < ROTATE_THRESHOLD) // 可隨機訪問或小於閾值 rotate1(list, distance); else // 否則 rotate2(list, distance); }
說明:也分為兩種情況進行處理,分別對應rotate1、rotate2。roate1函式具體如下
private static <T> void rotate1(List<T> list, int distance) { // 取得集合大小 int size = list.size(); if (size == 0) // 集合為空,返回 return; distance = distance % size; // 取模操作 if (distance < 0) // 為負數,之後保證為正數 distance += size; if (distance == 0) // 移動距離為0,直接返回 return; // 遍歷集合 for (int cycleStart = 0, nMoved = 0; nMoved != size; cycleStart++) { T displaced = list.get(cycleStart); int i = cycleStart; do { i += distance; if (i >= size) i -= size; displaced = list.set(i, displaced); nMoved ++; } while (i != cycleStart); } }
說明:這個演算法特別的巧妙,可作為面試考點。rotate2函式具體如下
private static void rotate2(List<?> list, int distance) { int size = list.size(); if (size == 0) return; int mid = -distance % size; if (mid < 0) mid += size; if (mid == 0) return; // 將AB變為BA,可以先對A求逆,再對B求逆,再對整體求逆,則可以得到BA // 先反向0到mid的元素 reverse(list.subList(0, mid)); // 再反向mid到size的元素 reverse(list.subList(mid, size)); // 最後反向整個表 reverse(list); }
說明:使用遞迴進行旋轉。
8. replaceAll函式
用於替換集合中所有指定元素。簽名如下
public static <T> boolean replaceAll(List<T> list, T oldVal, T newVal)
具體程式碼如下
public static <T> boolean replaceAll(List<T> list, T oldVal, T newVal) { boolean result = false; // 集合大小 int size = list.size(); if (size < REPLACEALL_THRESHOLD || list instanceof RandomAccess) { // 小於替換閾值或者可以隨機訪問 if (oldVal==null) { // 舊值為空 // 遍歷集合 for (int i=0; i<size; i++) { if (list.get(i)==null) { // 為空就設定為新值 list.set(i, newVal); result = true; } } } else { // 舊值不為空 // 遍歷集合 for (int i=0; i<size; i++) { if (oldVal.equals(list.get(i))) { // 與舊值相等就設定為新值 list.set(i, newVal); result = true; } } } } else { // 否則 // 獲取迭代器 ListIterator<T> itr=list.listIterator(); if (oldVal==null) { // 舊值為空 for (int i=0; i<size; i++) { // 遍歷 if (itr.next()==null) { itr.set(newVal); result = true; } } } else { // 舊值不為空 for (int i=0; i<size; i++) { if (oldVal.equals(itr.next())) { // 與舊值相等就設定為新值 itr.set(newVal); result = true; } } } } return result; }
說明:可以替換空值null。也是分兩種情況進行處理。
9. indexOfSubList函式
用於在指定集合索引子集合,成功,則返回位置,不成功,則返回-1。簽名如下
public static int indexOfSubList(List<?> source, List<?> target)
具體程式碼如下
public static int indexOfSubList(List<?> source, List<?> target) { // 源集合大小 int sourceSize = source.size(); // 目標集合大小 int targetSize = target.size(); // 大小差 int maxCandidate = sourceSize - targetSize; if (sourceSize < INDEXOFSUBLIST_THRESHOLD || (source instanceof RandomAccess&&target instanceof RandomAccess)) { // 小於子集索引閾值或者(源集合與目標集合都支援隨機訪問) nextCand: for (int candidate = 0; candidate <= maxCandidate; candidate++) { // 只需要遍歷從0到maxCandidate即可 for (int i=0, j=candidate; i<targetSize; i++, j++) if (!eq(target.get(i), source.get(j))) // 不相等 continue nextCand; // 不匹配,又回到for,此時將不會執行int candidate = 0操作 return candidate; // 全部匹配,返回索引 } } else { // 否則,使用迭代器操作 // 獲取迭代器 ListIterator<?> si = source.listIterator(); nextCand: for (int candidate = 0; candidate <= maxCandidate; candidate++) { ListIterator<?> ti = target.listIterator(); for (int i=0; i<targetSize; i++) { if (!eq(ti.next(), si.next())) { // 不相等 // 回溯源集合迭代器 for (int j=0; j<i; j++) si.previous(); continue nextCand; // 又回到for,此時將不會執行int candidate = 0操作 } } return candidate; // 全部匹配,返回索引 } } return -1; // 不匹配,返回-1 }
說明:也是分為兩種情況進行處理。
10. frequency函式
用來統計一個元素在集合中出現的次數。簽名如下
public static int frequency(Collection<?> c, Object o)
具體程式碼如下
public static int frequency(Collection<?> c, Object o) { int result = 0; if (o == null) { // 物件為空 // 遍歷集合 for (Object e : c) if (e == null) // 為空 result++; } else { // 物件不為空 // 遍歷集合 for (Object e : c) if (o.equals(e)) result++; } return result; }
說明:可以對空值null進行統計。
11. reverseOrder函式
反轉比較邏輯,即反轉集合順序,有兩個過載函式,簽名如下
public static <T> Comparator<T> reverseOrder() public static <T> Comparator<T> reverseOrder(Comparator<T> cmp)
函式返回型別為Comparator型別,該Comparator的比較邏輯與之前的比較邏輯相反。reverseOrder函式程式碼如下
public static <T> Comparator<T> reverseOrder() { return (Comparator<T>) ReverseComparator.REVERSE_ORDER; }
說明:另外一個過載函式與此類似,不再累贅。
12. addAll函式
用於向集合中新增多個元素,簽名如下
public static <T> boolean addAll(Collection<? super T> c, T... elements)
說明:第二個引數為變長引數,即可以傳遞多個值。具體程式碼如下
public static <T> boolean addAll(Collection<? super T> c, T... elements) { boolean result = false; for (T element : elements) result |= c.add(element); return result; }
Collections的主要方法就分析到這裡。下面分析Arrays類的方法。
三、Arrays原始碼分析
3.1 類的屬性
public class Arrays { // 可以進行並行排序的最小陣列長度 private static final int MIN_ARRAY_SORT_GRAN = 1 << 13; }
3.2 建構函式
private Arrays() {}
說明:私有建構函式,類外不允許呼叫。
3.3 方法分析
Arrays的全部方法如下
說明:可以看到,Arrays工具類處理的是陣列型別。並且每個方法存在多個過載版本。
3.4 核心方法分析
由於存在多個過載版本,每個過載版本的邏輯大體一致,故只分析有代表性的版本。
1. sort函式
用於對陣列進行排序,兩個主要的過載版本,方法簽名如下
public static void sort(int[] a) public static void sort(int[] a, int fromIndex, int toIndex)
說明:第一個版本是對整個陣列進行排序,第二個版本只對指定部分進行排序。第一個版本程式碼如下
public static void sort(int[] a) { // 使用快速排序 DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0); }
說明:使用快速排序進行排序。第二個版本與第一個版本類似,不再累贅。
2. equals方法
用於判斷兩個陣列是否相等,若陣列元素重寫了equals方法,則按照元素的equals方法進行比較。主要的過載版本,方法簽名如下
public static boolean equals(int[] a, int[] a2) public static boolean equals(Object[] a, Object[] a2)
說明:第一個版本具體程式碼如下
public static boolean equals(int[] a, int[] a2) { if (a==a2) return true; if (a==null || a2==null) // 任一陣列為空,則返回false return false; int length = a.length; if (a2.length != length) // 陣列大小不相等,返回false return false; for (int i=0; i<length; i++) // 遍歷比較,元素全部相等則返回true,否則,返回false if (a[i] != a2[i]) return false; return true; }
說明:首先,判斷是否為同一個陣列引用,滿足後,再判斷是否有陣列為空,滿足後,再判斷兩陣列長度是否相等,滿足後,最後遍歷陣列進行比較。
3. copyOf函式
用於複製陣列元素至另一個陣列。主要過載版本,簽名如下
public static <T> T[] copyOf(T[] original, int newLength) public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType)
第二個版本,具體程式碼如下
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) { @SuppressWarnings("unchecked") T[] copy = ((Object)newType == (Object)Object[].class) ? (T[]) new Object[newLength] : (T[]) Array.newInstance(newType.getComponentType(), newLength); System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); return copy; }
4. copyOfRange函式
指定陣列一段元素進行復制(指定了開始位置和結束位置),簽名如下
public static <T> T[] copyOfRange(T[] original, int from, int to) public static <T,U> T[] copyOfRange(U[] original, int from, int to, Class<? extends T[]> newType)
第二個版本,具體程式碼如下
public static <T,U> T[] copyOfRange(U[] original, int from, int to, Class<? extends T[]> newType) { int newLength = to - from; if (newLength < 0) throw new IllegalArgumentException(from + " > " + to); @SuppressWarnings("unchecked") T[] copy = ((Object)newType == (Object)Object[].class) ? (T[]) new Object[newLength] : (T[]) Array.newInstance(newType.getComponentType(), newLength); System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength)); return copy; }
5. hashCode函式
求得陣列的hashCode,主要版本,簽名如下
public static int hashCode(int a[])
程式碼如下
public static int hashCode(int a[]) { if (a == null) return 0; int result = 1; for (int element : a) result = 31 * result + element; return result; }
6. asList函式
用於將不定引數轉化為List,方法簽名如下
public static <T> List<T> asList(T... a)
具體程式碼如下
public static <T> List<T> asList(T... a) { return new ArrayList<>(a); }
7. toString函式
用於更友好的顯示陣列資訊,簽名如下
public static String toString(int[] a)
注意,並沒有覆蓋Object類的toString方法,因為方法簽名不相同。具體程式碼如下
public static String toString(int[] a) { if (a == null) return "null"; int iMax = a.length - 1; if (iMax == -1) return "[]"; StringBuilder b = new StringBuilder(); b.append('['); for (int i = 0; ; i++) { b.append(a[i]); if (i == iMax) return b.append(']').toString(); b.append(", "); } }
Arrays工具類的主要方法就介紹到這裡,平時我們可以多用用裡面的方法,達到更熟悉的效果。
四、總結
Collections與Arrays提供了很多有用的方法,我們平時可以多用用,至此,集合框架的主要原始碼就分析完了。下面會接著分析併發框架的原始碼,謝謝各位園友的觀看~