一丶概述
Collections在之前关于集合的文章中有说到Collections是集合工具类,提供一些相关算法,然而从事Android开发,数据基本都由后台处理,很少用到,面试问到话绝对答不上,这里就说说源码与用法。
二丶常见案例
/** * <pre> * author : JinBiao * CSDN : http://my.csdn.net/DT235201314 * time : 2017/12/11 * desc : 集合工具类Collections 常见方法demo * version: 1.0 * </pre> */public class CollectionsDemo { public static void main(String[] args){ ArrayList nums=new ArrayList(); nums.add(12); nums.add(-5); nums.add(8); Collections.sort(nums);//[-5, 8, 12] System.out.println(nums); nums.add(7); Collections.reverse(nums); System.out.println(nums);//[7, 12, 8, -5] Collections.shuffle(nums);//随机排序 System.out.println(nums);//假定此刻为:[12, 7, -5, 8] nums.add(10); System.out.println(nums);//[12, 8, 7, -5, 10] Collections.rotate(nums, 3); //rotate操作,正数是将nums的后3个数整体搬移到前面,负数是将前面3个数整体搬移到后面。 System.out.println(nums);//[7, -5, 10, 12, 8] Collections.rotate(nums, -2); System.out.println(nums);//[10, 12, 8, 7, -5] //查找替换操作 nums.add(7); System.out.println(Collections.max(nums));//12 System.out.println(Collections.min(nums));//-5 Collections.replaceAll(nums, 7, 9);//将num中所有7替换为9[10, 12, 8, 9, -5, 9] System.out.println(nums); System.out.println(Collections.frequency(nums, 9));//2 Collections.sort(nums);//只有排序了才能用二分查找 System.out.println(Collections.binarySearch(nums, 10));//4 } }/**运行结果: [-5, 8, 12] [7, 12, 8, -5] [7, 12, -5, 8] [7, 12, -5, 8, 10] [-5, 8, 10, 7, 12] [10, 7, 12, -5, 8] 12 -5 [10, 9, 12, -5, 8, 9] 2 4 */
三丶源码分析
(1)sort()排序方法
/** * List中的所有元素必须实现Compareable接口,即每个 元素必须是可比的。 * 算法的实现原理为: 把指定的List转化为一个对象数组,对数组进行排序,然后迭代List的每一个元素, * 在同样的位置重新设置数组中排好序的元素 */public static <T extends Comparable<? super T>> void sort(List<T> list) { if (list.getClass() == ArrayList.class) { //transient Object[] elementData // 用transient关键字标记的成员变量不参与序列化过程。 Arrays.sort(((ArrayList) list).elementData, 0, list.size()); return; } Object[] a = list.toArray(); Arrays.sort(a); ListIterator<T> i = list.listIterator(); for (int j=0; j<a.length; j++) { i.next(); i.set((T)a[j]); } }
/** * 传一个实现了Comparator接口的对象进来。 c.compare(o1,o2);来比较两个元素 */public static <T> void sort(List<T> list, Comparator<? super T> c) { if (list.getClass() == ArrayList.class) { Arrays.sort(((ArrayList) list).elementData, 0, list.size(), (Comparator) c); return; } Object[] a = list.toArray(); Arrays.sort(a, (Comparator)c); ListIterator<T> i = list.listIterator(); for (int j=0; j<a.length; j++) { i.next(); i.set((T)a[j]); } }
Comparable && Comparator区别与源码分析
(2)binarySearch()二分查找方法
/** * 使用二分查找在指定List中查找指定元素key。 List中的元素必须是有序的。如果List中有多个key,不能确保哪个key值被找到。 * 如果List不是有序的,返回的值没有任何意义 * * 对于随机访问列表来说,时间复杂度为O(log(n)),比如1024个数只需要查找log2(1024)=10次, * log2(n)是最坏的情况,即最坏的情况下都只需要找10次 * 对于链表来说,查找中间元素的时间复杂度为O(n),元素比较的时间复杂度为O(log(n)) * * @return 查找元素的索引。如果返回的是负数表明找不到此元素,但可以用返回值计算 * 应该将key插入到集合什么位置,任然能使集合有序(如果需要插入key值的话)。 公式:point = -i - 1 * */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); }
/** * 使用索引化二分查找。 size小于5000的链表也用此方法查找 */private static <T>int indexedBinarySearch(List<? extends Comparable<? super T>> list, T key) { int low = 0; int high = list.size()-1; while (low <= high) { // >>>1:无符号右移,忽略符号位,空位都以0补齐 int mid = (low + high) >>> 1;//相当于无符号除以2 Comparable<? super T> midVal = list.get(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>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; }
/** * 提供实现了Comparator接口的对象比较元素 */public static <T> int binarySearch(List<? extends T> list, T key, Comparator<? super T> c) { if (c==null) return binarySearch((List<? extends Comparable<? super T>>) list, key); if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD) return Collections.indexedBinarySearch(list, key, c); else return Collections.iteratorBinarySearch(list, key, c); }private static <T> int indexedBinarySearch(List<? extends T> l, T key, Comparator<? super T> c) { int low = 0; int high = l.size()-1; while (low <= high) { int mid = (low + high) >>> 1; T midVal = l.get(mid); int cmp = c.compare(midVal, 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> int iteratorBinarySearch(List<? extends T> l, T key, Comparator<? super T> c) { int low = 0; int high = l.size()-1; ListIterator<? extends T> i = l.listIterator(); while (low <= high) { int mid = (low + high) >>> 1; T midVal = get(i, mid); int cmp = c.compare(midVal, 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}
(3)reverse()反序方法
/** * 逆序排列指定列表中的元素 */public static void reverse(List<?> list) { int size = list.size(); //size小于18的链表或是基于随机访问的列表 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(); 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); } } }
/** * 交换List中两个位置的值 */public static void swap(List<?> list, int i, int j) { final List l = list; l.set(i, l.set(j, l.get(i))); }
(4)shuffle()随机混排方法
/** * 对指定列表中的元素进行混排 */public static void shuffle(List<?> list) { Random rnd = r; if (rnd == null) r = rnd = new Random(); // harmless race. shuffle(list, rnd); }private static Random r;
/** * 提供一个随机数生成器对指定List进行混排 * 基本算法思想为: 逆向遍历list,从最后一个元素到第二个元素,然后重复交换当前位置 与随机产生的位置的元素值。 * 如果list不是基于随机访问并且其size>5,会先把List中的复制到数组中, 然后对数组进行混排,再把数组中的元素重新填入List中。 * 这样做为了避免迭代器大跨度查找元素影响效率 */public static void shuffle(List<?> list, Random rnd) { int size = list.size(); if (size < SHUFFLE_THRESHOLD || list instanceof RandomAccess) { for (int i=size; i>1; i--) // 从i-1个位置开始与随机位置元素交换值 swap(list, i-1, rnd.nextInt(i)); } else { Object arr[] = list.toArray(); for (int i=size; i>1; i--) // 对数组进行混排 swap(arr, i-1, rnd.nextInt(i)); // 然后把数组中的元素重新填入List ListIterator it = list.listIterator(); for (int i=0; i<arr.length; i++) { it.next(); it.set(arr[i]); } } }
/** * 交换数组中两个位置的值 */private static void swap(Object[] arr, int i, int j) { Object tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; }
(5)fill()替换方法
/** * 用obj替换List中的所有元素 size<25 依次遍历赋值即可 */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); } } }
(6)copy()复制方法
/** * 复制源列表的所有元素到目标列表, 如果src.size > dest.size 将抛出一个异常 如果src.size < dest.size * dest中多出的元素将不受影响 同样是依次遍历赋值 */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()); } } }
(7)min()最小值法
/** * 返回集合中的最小元素。前提是其中的元素都是可比的,即实现了Comparable接口 * 反正要依次遍历完所有元素,所以直接用了迭代器 */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; }
/** * 根据提供的比较器求最小元素 */public static <T> T min(Collection<? extends T> coll, Comparator<? super T> comp) { if (comp==null) return (T)min((Collection) coll); Iterator<? extends T> i = coll.iterator(); T candidate = i.next(); while (i.hasNext()) { T next = i.next(); if (comp.compare(next, candidate) < 0) candidate = next; } return candidate; }
(8)max()最大值方法
/** *最大元素方法 同最小值 */public static <T extends Object & Comparable<? super T>> T max(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; }
public static <T> T max(Collection<? extends T> coll, Comparator<? super T> comp) { if (comp==null) return (T)max((Collection) coll); Iterator<? extends T> i = coll.iterator(); T candidate = i.next(); while (i.hasNext()) { T next = i.next(); if (comp.compare(next, candidate) > 0) candidate = next; } return candidate; }
(9)rotate()轮换方法
/** * 旋转移位List中的元素通过指定的distance。每个元素移动后的位置为: (i + * distance)%list.size.此方法不会改变列表的长度 * 比如,类表元素为: [t, a, n, k, s , w] 执行Collections.rotate(list, 2)或 * Collections.rotate(list, -4)后, list中的元素将变为 [s, w, t, a, n , * k]。可以这样理解:正数表示向后移,负数表示向前移 */public static void rotate(List<?> list, int distance) { if (list instanceof RandomAccess || list.size() < ROTATE_THRESHOLD) rotate1(list, distance); else rotate2(list, distance); }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) return; for (int cycleStart = 0, nMoved = 0; nMoved != size; cycleStart++) { T displaced = list.get(cycleStart); int i = cycleStart; do { i += distance; // 超出size就减去size if (i >= size) i -= size; displaced = list.set(i, displaced); nMoved ++; } while (i != cycleStart); } }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; //这都可以,才发现 reverse(list.subList(0, mid)); reverse(list.subList(mid, size)); reverse(list); }
(10)replaceAll()替换,有改变返回true
/** * 把指定集合中所有与oladVal相等的元素替换成newVal 只要list发生了改变就返回true */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; }
(11)int indexOfSubList 是否包含字符串
/** * * target是否是source的子集,如果是返回target第一个元素的索引, 否则返回-1。 * 其实这里和串的模式匹配差不多。这里使用的是基本的回溯法。 * */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++) { for (int i=0, j=candidate; i<targetSize; i++, j++) if (!eq(target.get(i), source.get(j))) continue nextCand; // Element mismatch, try next cand return candidate; // All elements of candidate matched target } } else { // Iterator version of above algorithm 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())) { // Back up source iterator to next candidate for (int j=0; j<i; j++) si.previous(); continue nextCand; } } return candidate; } } return -1; // No candidate matched the target}
static boolean eq(Object o1, Object o2) { return o1==null ? o2==null : o1.equals(o2); }
作者:天一方蓝
链接:https://www.jianshu.com/p/46fdf4417666