上一章简单介绍了 JavaSE实现任务调度的三种方式(十六),如果没有看过,请观看上一章
一. Arrays 工具类
一.一 为什么出现 Arrays 工具类
在开发当中,虽然经常使用的是集合维护数据,但是数组的使用频率也是很高的,尤其是在 Java SE 阶段。
数组,常见的有 添加数据,删除数据,更新数据,搜索数据,排序, 获取长度,数组最大/小值 等很多内容。
在 JDK 1.2 版本时, 为了方便维护数组, 增加了一个 Arrays 的工具类。
Arrays 类 位于 java.utils 包下。
一.二 Arrays 工具类的常见方法
数组,有多种类型的数组, int,float,char,boolean 等, 所以Arrays 工具类中里面有很多的重载方法。 这里只以最常用的 int 类型进行举例。
方法签名 | 方法作用 |
转换成集合: | |
public static <T> List<T> asList(T… a) | 将可变参数数组转换成集合 |
打印输出: | |
public static String toString(int[] a) | 输出数组中的内容 |
查询搜索的: | |
public static int binarySearch(int[] a, int key) | 从数组a 里面查询key ,返回索引位置。 |
public static int binarySearch(int[] a, int fromIndex, int toIndex,int key) | 从数组a 的fromIndex 到 toIndex 索引位置 查询 key,限定了查询的范围 |
复制数组的: | |
public static int[] copyOf(int[] original, int newLength) | 复制数组original, 从0索引开始,长度为newLength,返回 一个新数组 |
public static int[] copyOfRange(int[] original, int from, int to) | 复制数组 original, 从from 开始,到to 结束, 不包括to, 返回一个新的数组。 是范围性复制 |
比较数组是否相同的: | |
public static boolean equals(int[] a, int[] a2) | 比较两个数组的内容是否一样,如果一样,返回true,不一样,返回false |
填充数组的: | |
public static void fill(int[] a, int val) | 将 val 的值填充到数组里面,全部替换 |
public static void fill(int[] a, int fromIndex, int toIndex, int val) | 只替换 fromIndex 到toIndex 的内容,不包括toIndex 处的元素 |
二元操作的: | |
public static void parallelPrefix(int[] array, IntBinaryOperator op) | 对数组 array 进行二元操作,具体看例子 |
public static void parallelSetAll(int[] array, IntUnaryOperator generator) | 对数组中每一个元素进行操作, 具体看例子 |
public static void setAll(int[] array, IntUnaryOperator generator) | 全部做表达式操作,具体看例子 |
排序的: | |
public static void parallelSort(int[] a) | 并发排序, 是升序 |
public static void parallelSort(int[] a, int fromIndex, int toIndex) | 对 fromIndex 到 toIndex 范围内的数字进行并发排序 |
public static void sort(int[] a) | 普通排序, 是升序 |
public static void sort(int[] a, int fromIndex, int toIndex) | 对fromIndex 到toIndex 范围内的数字进行排序 |
转换成流: | |
public static IntStream stream(int[] array) | 将数组转换成流 |
public static IntStream stream(int[] array, int startInclusive, int endExclusive) | 将 startInclusive 到 endExclusive 的部分转换成流 |
二. Arrays 工具类的具体使用
二.一 转换成集合 asList(T … a)
二.一.一 例子
@Test public void asListTest(){ //转换成集合 List<Integer> arrList=Arrays.asList(1,2,3,4,5,6,7,8,9); System.out.println("转换成后长度:"+arrList.size()+",内容是:"+arrList.toString()); }
控制台打印输出:
二.一.二 简单内部实现
注意,内部构造的 ArrayList 并不是 java.util.ArrayList, 而是 Arrays 内部类 ArrayList.
二.二 打印内容 toString(int[] a)
二.二.一 例子
@Test public void toStringTest(){ //数组 int[] arr={1,2,3,4,5,6,7,8,9}; //打印内容 String str=Arrays.toString(arr); System.out.println("打印内容:"+str); }
控制台打印输出:
二.二.二 简单内容实现
public static String toString(int[] a) { //为空时, 返回 null if (a == null) return "null"; //长度为0 时, 返回 [] 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(", "); } } }
二.三 查询搜索 binarySearch(int[] a, int key)
二.三.一 无范围的例子
@Test public void binarySearchTest1(){ //数组 int[] arr={1,2,3,4,5,6,7,8,9}; int index=Arrays.binarySearch(arr,5); System.out.println("查询5的索引:"+index); index=Arrays.binarySearch(arr,10); System.out.println("查询10的索引:"+index); }
控制台打印输出:
内部实现,调用的是 有范围的查询, 查询的是 从0 到 arr.length 范围的
二.三.二 有范围的例子
@Test public void binarySearchTest2(){ //数组 int[] arr={1,2,3,4,5,6,7,8,9}; int index=Arrays.binarySearch(arr,4,8,5); System.out.println("查询5的索引:"+index); index=Arrays.binarySearch(arr,4,8,9); System.out.println("查询不在范围内的9的索引:"+index); }
控制台打印输出:
二.三.三 简单内部实现
private static int binarySearch0(int[] a, int fromIndex, int toIndex, int key) { int low = fromIndex; //不包括 toIndex 索引处的位置 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 } // 如果没有查找到, 返回的是 - (low+1), 并不一定是-1 return -(low + 1); // key not found. }
以 无范围的 10 例子进行举例, 传入过来的参数依次是: 1~9的数组,0,9,10.
那么 low=0, hight=8;
第一次循环: 0<=8, 成立。 mid=(0+8)/2=4, midVal=5, 5<10, 此时 low=4+1=5;
第二次循环: 5<=8, 成立, mid=(5+8)/2=6, midVal=7, 7<10, 此时 low=6+1=7;
第三次循环: 7<=8, 成立, mid=(7+8)/2=7, midVal=8, 8<10, 此时 low=7+1=8;
第四次循环: 8<=8, 成立, mid=(8+8)/2=8, midVal=9,9<10,此时 low=8+1=9;
9<=8, 不成立, 退出循环。 此时, low=9
返回 -(9+1)= -10, 最后结果返回 -10.
二.四 复制数组 copyOf 和范围复制数组 copyOfRange
二.四.一 copyOf 小例子
@Test public void copyOfTest(){ //数组 int[] arr={1,2,3,4,5,6,7,8,9}; //只复制前5个 int[] newArr=Arrays.copyOf(arr,5); System.out.println("newArr新数组长度:"+newArr.length+",内容:"+Arrays.toString(newArr)); }
控制台打印输出:
内部调用的是 System 类的 arraycopy() 方法。
二.四.二 copyOfRange 小例子
@Test public void copyOfRnageTest(){ //数组 int[] arr={1,2,3,4,5,6,7,8,9}; //只复制 3~7 索引的位置。 不包括7 索引处的元素 int[] newArr=Arrays.copyOfRange(arr,3,7); System.out.println("newArr新数组长度:"+newArr.length+",内容:"+Arrays.toString(newArr)); }
控制台打印输出:
我们发现,内部调用的依然是 System 类的 arraycopy() 方法。
那么,接下来,我们就分析一下, System 类的 arraycopy() 方法 是如何实现数组复制的。
二.四.三 System 类的 arraycopy()
System 类位于 java.lang 包下, System 类用 final 进行修饰
public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
该方法 arraycopy 用 native 进行修饰,是用其他底层语言写的,可以快速进行数组复制。
这儿看不到具体的实现, 我们就仔细分析一下,各个参数吧。
src, 源数组。 即复制源是谁
srcPos, 从源数组的哪儿开始复制, 即复制的开始索引
dest ,目的数组, 即复制目的地是谁
destPos, 目的数组的源头在哪,即复制目的地的开始索引。 不一定是从0开始复制,支持中间索引位置处复制
length, 复制的长度。
这五个参数的顺序不能搞错。
copyOf() 方法时,传入过来的是 srcPos=0, destPos=0, length= Math.min(arr.length,newLength), 最小的那个长度。
表示从头开始复制。 如果newLength> arr.length 的话, 那么从 arr.length~newLength 处的值为默认值。
copyOfRange() 方法时,传入过来的是 srcPos=from, destPos=0,length=Math.min(arr.length-from, to-from)
二.四.四 System 类的 arraycopy() 方法举例
@Test public void arraycopyTest(){ //源数组 int[] arr={1,2,3,4,5,6,7,8,9}; //目标 int [] dest={100,101,102,103,104,105,106,107,108,109,110}; // 从103处开始复制, 拿过来的是 开始位置是5, 拿5个。 System.arraycopy(arr,4,dest,3,5); // System.arraycopy(arr,4,dest,3,5);: 新复制的长度:11,内容:[100, 101, 102, 5, 6, 7, 8, 9, 108, 109, 110] //System.arraycopy(arr,4,dest,3,6); java.lang.ArrayIndexOutOfBoundsException System.out.println("新复制的长度:"+dest.length+",内容:"+Arrays.toString(dest)); }
控制台打印输出:
需要注意,两个数组, 源数组 src, 目标数组 dest 的原始长度可能不一致, 复制的开始位置也可能不一致,而复制的长度是一致的, 而数组 dest的长度是无法改变的, 所以很有可能造成 ArrayIndexOutOfBoundsException 异常。
要保证: srcPos+length<= src.length; destPos+length<=dest.length;
二.五 比较数组是否相同 equals()
二.五.一 例子
@Test public void equalsTest(){ //源数组 int[] arr1={1,2,3,4,5,6,7,8,9}; int[] arr2={1,2,3,4,5}; int[] arr3={1,2,3,4,5,6,7,8,9}; System.out.println("arr1与 arr2是否相同:"+Arrays.equals(arr1,arr2)); System.out.println("arr1与 arr3是否相同:"+Arrays.equals(arr1,arr3)); }
控制台打印输出:
也可以比较 对象数组, 需要重写对象的 equals () 方法。
二.五.二 简单内部实现
二.六 填充数组 fill
二.六.一 无范围填充
@Test public void fillTest1(){ //源数组有内容填充: int[] arr1={1,2,3,4,5,6,7,8,9}; //填充100 Arrays.fill(arr1,100); System.out.println("有内容数组填充:"+Arrays.toString(arr1)); int[] arr2=new int[9]; Arrays.fill(arr2,100); System.out.println("无内容数组填充:"+Arrays.toString(arr2)); }
控制台打印输出:
实际上,就是替换。
内部实现:
public static void fill(int[] a, int val) { for (int i = 0, len = a.length; i < len; i++) a[i] = val; }
二.六.二 有范围填充
@Test public void fillTest2(){ //源数组有内容填充: int[] arr1={1,2,3,4,5,6,7,8,9}; //填充100,只填充2~5 索引处的内容 Arrays.fill(arr1,2,5,100); System.out.println("有内容数组2~5填充:"+Arrays.toString(arr1)); int[] arr2=new int[9]; Arrays.fill(arr2,2,5,100); System.out.println("无内容数组2~5填充:"+Arrays.toString(arr2)); }
控制台打印输出:
内部实现, 范围性替换
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; }
二.七 二元操作
类似于 遍历数组中的每一个元素,执行相同的维护操作。
二.七.一 parallelPrefix() 小例子
如,使数组中的每一个元素,都变成 当前元素* 上一个索引处的元素, 即改变每一个元素的值。
@Test public void parallelPrefixTest(){ int[] arr={1,2,3,4,5,6,7,8,9}; Arrays.parallelPrefix(arr, new IntBinaryOperator() { //left代表数组中前一个索引处的元素值,第一个元素,不进入该方法计算 //right代表数组中当前索引处的元素值 @Override public int applyAsInt(int left, int right) { return left*right; } }); System.out.println("新内容为:"+Arrays.toString(arr)); }
控制台打印输出:
二.七.二 parallelSetAll() 小例子
用于重新设置数组中的元素值
@Test public void parallelSetAllTest(){ int[] arr={1,2,3,4,5,6,7,8,9}; Arrays.parallelSetAll(arr, new IntUnaryOperator() { @Override public int applyAsInt(int operand) { return operand*2; } }); System.out.println("有内容的新内容为:"+Arrays.toString(arr)); arr=new int[9]; Arrays.parallelSetAll(arr, new IntUnaryOperator() { @Override public int applyAsInt(int operand) { return operand*2; } }); System.out.println("无内容的新内容为:"+Arrays.toString(arr)); }
控制台打印输出:
二.七.三 setAll() 小例子
@Test public void setAllTest(){ int[] arr={1,2,3,4,5,6,7,8,9}; Arrays.setAll(arr, new IntUnaryOperator() { @Override public int applyAsInt(int operand) { return operand*3; } }); System.out.println("有内容的新内容为:"+Arrays.toString(arr)); arr=new int[9]; Arrays.setAll(arr, new IntUnaryOperator() { @Override public int applyAsInt(int operand) { return operand*3; } }); System.out.println("无内容的新内容为:"+Arrays.toString(arr)); }
控制台打印输出:
二.八 排序 sort()
二.八.一 parallelSort() 并发无范围排序 小例子
@Test public void parallelSortTest1(){ int[] arr={1,3,5,7,9,2,4,6,8}; Arrays.parallelSort(arr); System.out.println("排序后的数组为:"+Arrays.toString(arr)); }
控制台打印输出:
二.八.二 parallelSort() 并发有范围排序 小例子
@Test public void parallelSortTest2(){ int[] arr={1,3,5,7,9,2,4,6,8}; //只排序 3~7, 即 7,9,2,4 四个元素。 排序完为 2,4,7,9 Arrays.parallelSort(arr,3,7); System.out.println("排序后的数组为:"+Arrays.toString(arr)); }
控制台打印输出:
二.八.三 sort() 无范围排序 小例子
@Test public void sortTest1(){ int[] arr={1,3,5,7,9,2,4,6,8}; Arrays.sort(arr); System.out.println("排序后的数组为:"+Arrays.toString(arr)); }
控制台打印输出:
二.八.四 sort() 有范围排序小例子
@Test public void sortTest2(){ int[] arr={1,3,5,7,9,2,4,6,8}; //只排序 3~7, 即 7,9,2,4 四个元素。 排序完为 2,4,7,9 Arrays.sort(arr,3,7); System.out.println("排序后的数组为:"+Arrays.toString(arr)); }
控制台打印输出:
二.九 转换成流 Stream()
二.九.一 无范围转换小例子
@Test public void streamTest1(){ int[] arr={1,2,3,4,5,6,7,8,9}; //转换成流 Arrays.stream(arr).forEach(n -> System.out.print(n)); }
控制台打印输出:
二.九.二 有范围转换小例子
@Test public void streamTest2(){ int[] arr={1,2,3,4,5,6,7,8,9}; //将索引位置为 3~8的转换成流,不包括8索引处的元素 Arrays.stream(arr,3,8).forEach(n -> System.out.print(n)); }
控制台打印输出:
谢谢您的观看,如果喜欢,请关注我,再次感谢 !!!