JDK1.7源码笔记之Arrays类
来源:互联网 发布:vscode 编码方式默认 编辑:程序博客网 时间:2024/05/18 05:52
1. class 简介
Arrays类包含很多对数组进行操作的静态方法,可以对数组进行复制、填充、排序、搜索、比较、打印和转换成集合容器等操作,除非特别说明,所有方法都会因为传入一个null的数组引用而抛出NullPointerException。Arrays类的方法频繁地出现在JDK源码(尤其是Java中的数据结构)中,所以我觉得弄清楚Arrays类的内部实现是有必要的,虽然本身就很简单。
2. class内部原理及特点
- Arrays类中的暴露在外部的所有方法都是静态方法,Arrays类不能被实例化。
- CopyOf方法底层清一色地使用了System.arraycopy方法。
- 对于基本类型数据的排序使用的是DualPivotQuickSort双支点快排。
- 对于引用类型和泛型数据的排序使用的是TimSort。
- 搜索即为最常见的二分搜索
3. class源码细节分析
所谓射人先射马,擒贼先擒王,JDK中的方法一般都重载了很多个,但是大多数方法底层都是调用了同一个最复杂的最具有一般性的方法。所以这里只贴最具代表性的。
copyOf 和 copyOfRange
这类方法分两种,一种是指定长度从下标0开始复制,另一种是指定复制的起始位置。
/* 把original数组的下标0到下标newLength-1间的所有元素复制到一个新数组并返回 */public static int[] copyOf(int[] original, int newLength) { int[] copy = new int[newLength]; System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); return copy;}
/* 这个方法使用Array类通过反射动态生成一个和original数组相同类型的数组,之后再将original数组的指定长度的元素复制过去 */public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) { 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; }
/* 下面两个方法指定复制的起始点,除此之外和copyOf方法一样 */public static int[] copyOfRange(int[] original, int from, int to) { int newLength = to - from; if (newLength < 0) throw new IllegalArgumentException(from + " > " + to); int[] copy = new int[newLength]; System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength)); return copy;}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); 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;}
以上这些copy方法都相当于对System.arraycopy进行了封装,直接使用System.arraycopy可能会因为newLength过长而下标越界抛出异常,而Arrays.copyOf和Arrays.copyOfRange则没有这个问题,它会使用两个数组中较短的长度。
sort
同样分成两种,一种是对整个数组进行排序,另一种是对于一个指定区间进行排序。
/* 对于int,short,long,double,float,byte,char等基本数据类型采用双支点快排,效率比传统的快排要好 */public static void sort(int[] a) { DualPivotQuicksort.sort(a);}public static void sort(int[] a, int fromIndex, int toIndex) { rangeCheck(a.length, fromIndex, toIndex); DualPivotQuicksort.sort(a, fromIndex, toIndex - 1);}
/* 对于引用类型和泛型采用TimSort */public static void sort(Object[] a) { if (LegacyMergeSort.userRequested) legacyMergeSort(a); else ComparableTimSort.sort(a);}public static void sort(Object[] a, int fromIndex, int toIndex) { if (LegacyMergeSort.userRequested) legacyMergeSort(a, fromIndex, toIndex); else ComparableTimSort.sort(a, fromIndex, toIndex);}public static <T> void sort(T[] a, Comparator<? super T> c) { if (LegacyMergeSort.userRequested) legacyMergeSort(a, c); else TimSort.sort(a, c);}public static <T> void sort(T[] a, int fromIndex, int toIndex, Comparator<? super T> c) { if (LegacyMergeSort.userRequested) legacyMergeSort(a, fromIndex, toIndex, c); else TimSort.sort(a, fromIndex, toIndex, c);}
binarySearch
/* 最常见的二分搜索,要求数组是排过序的,每种数据类型都重载了,但是核心是这个私有方法 */private static int binarySearch0(int[] a, int fromIndex, int toIndex, int key) { int low = fromIndex; int high = toIndex - 1; while (low <= high) { int mid = (low + high) >>> 1; int midVal = a[mid]; if (midVal < key) low = mid + 1; else if (midVal > key) high = mid - 1; else return mid; // key found } return -(low + 1); // key not found.}
equals
/* 浅层次的相等比较,无非就是先比较引用,再比较长度,再比较内容,重载的其它几个方法都一样 */public static boolean equals(int[] a, int[] a2) { if (a==a2) return true; if (a==null || a2==null) return false; int length = a.length; if (a2.length != length) return false; for (int i=0; i<length; i++) if (a[i] != a2[i]) return false; return true;}
fill
/* 填充数组,即循环赋值,重载的其它几个方法都一样 */public static void fill(int[] a, int val) { for (int i = 0, len = a.length; i < len; i++) a[i] = val;}public static void fill(int[] a, int fromIndex, int toIndex, int val) { rangeCheck(a.length, fromIndex, toIndex); for (int i = fromIndex; i < toIndex; i++) a[i] = val;}
toString
/* 浅层次的将数组字符串化(主要用于一维数组,二维以上用deepToString),两头加上中括号,中间以逗号间隔,其它重载的方法都一样 */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(", "); }}
hashCode
/* 使用的是Times31哈希函数 */public static int hashCode(int a[]) { if (a == null) return 0; int result = 1; for (int element : a) result = 31 * result + element; return result;}
asList
这个方法是数组到集合容器之间的桥梁,反过来集合容器可以通过toArray方法转换成数组
/* 这个ArrayList类是Arrays的一个内部类,注意其内部数组是不可变的 */public static <T> List<T> asList(T... a) { return new ArrayList<>(a);}
这个方法要注意两点:
1、Arrays.asList接受一个可变长参数,可以接受多个值或者一个数组,但如果是基本类型数组如int[]则并不会得到想要的结果,因为基本类型不能作为泛型参数,这里传入int[]数组只会被当成是一个参数,这样产生的集合容器中只会有一个元素,想要将数组转换为集合,必须是引用类型如Integer[]。
2、方法中的ArrayList是Arrays类的一个内部类,其内部数组是final不可变的,所以试图改变其内部数组容量的操作都会抛UnsupportedOperationException,比如:
Arrays类的内部类ArrayList中的数组定义如下: private final E[] a;public static void main(String args[]){ List<Integer> list = Arrays.asList(1,2,3,4,5); list.set(1,10)//没有改变容量,没有问题 list.add(6);//异常,此时的list内部数组为final,扩容会生成一个新数组 //如果想对这个list进行操作最好是将其作为一个新的list的构造参数 List<Integer>list2 = new ArrayList<Integer>(list);}
4. 总结
Arrays类的内部实现都非常的简单易懂,除了DualPivotQuicksort和TimSort可能要去查阅相关资料,其它的地方都没有什么的难点。在后面的集合类源码中,将会经常出现Arrays.copyOf和System.arraycopy方法。
- JDK1.7源码笔记之Arrays类
- JDK1.7源码笔记之String类
- JDK1.7源码笔记之ArrayList类
- JDK1.7源码笔记之LinkedList类
- JDK1.7源码笔记之ArrayDeque类
- JDK1.7-Arrays源码详解
- JDK1.7源码笔记之StringBuilder和StringBuffer类
- jdk1.5 Arrays 学习笔记
- Java Collections Framework之Arrays(method:sort(),binarySearch(),copyOf())部分源码分析(基于JDK1.6)
- Java Arrays 源码 笔记
- java 源码之 Arrays 工具类
- JDK 1.7源码阅读笔记(四)集合类之Arrays
- JDK1.7之 HashMap 源码分析
- Java学习笔记之Arrays工具类
- JDK1.6 源码学习笔记
- JDK源码阅读之Arrays
- Java源码分析之Arrays
- Java类集框架之HashMap(JDK1.8)源码剖析
- Android Studio 如何修改应用程序的版本号targetSdkVersion和Application Id
- IMWeb训练营第一天
- Yaf学习笔记之钩子与插件
- vlc
- 使用pandas读取csv文件指定的前几行
- JDK1.7源码笔记之Arrays类
- 关于HTML的学习
- 剪邮票
- 关于Flask mega-tutorial遇到的一些问题(外网访问+OpenId)
- Git提交代码简单指令
- POJ 1157 dp
- JAVA OOP第八章JDBC 上机+课后
- 函数柯里化
- HM学习二