JAVA开发工具包—JDK 中为我们提供了一个专门用于操作数组的工具类,即Arrays类,位于java.until包中。该类中的方法均为static修饰的静态方法,可以直接通过Arrays.xxx(xxx)的形式调用方法。主要提供了数组元素的复制、填充、比较、排序、查询等。Arrays工具类大多数是对数组进行的简化操作,ArrayList的底层也是基于数组实现的。
1.数组复制与创建
Arrays.copyOf( )
作用
基于复制
原数组的基础上创建一个新的数组
,可指定长度
(从头开始计算长度)。
创建一个新的数组,该数组是原始数组的副本,并且可能具有不同的长度。
使用方法 :先定义一个新数组,在通过Arrays.copyOf将原数组所需要的内容复制到新数组上(可指定长度,从头复制)eg: String[] 新数组名 = Arrays.copyOf(原数组名 新数组长度);
见下图,Arrays.copyOf 共有10中重载方法,其对应的参数类型不同,即数组类型不同,包含了基本类型和任意(自定义)引用类型
,最终的实现都是一个核心的方法System.arraycopy
,这是一个native方法
,不需要深究。
参数
T[] original:要复制的原始数组(T[]:数组类型 original:原数组名)。
int newLength,新数组的长度
返回值
返回值为数组,即根据你指定长度复制的数组
举例
String[] 新数组名 = Arrays.copyOf(原数组名 新数组长度);
“String[] 新数组名”用于存放复制的数组(返回值)
“ Arrays.copyOf(原数组名 新数组长度)”调用方法
import java.util.Arrays;
public class demo01 {
public static void main(String[] args) {
//原数组
String[] n1 = {"1","2","3"};
//调用方法
String[] n2 = Arrays.copyOf(n1,2);
//输出原数组
System.out.println(Arrays.toString(n1));
//输出新数组
System.out.println(Arrays.toString(n2));
}
}
运行结果:
注:若定长度超出原数组长度,则多出元素为默认值
源代码解读
他的各种类型都是雷同的,我们看一个int类型的。
1.根据用户指定的新数组长度创建了一个数组copy,用来存储我们将复制的数据。
2.调用System.arraycopy(原数组 起始下标,新数组,起始下标,要复制的元素数量)方法将原数组内容复制到新数组里。
其中“original,0”指定原数组和从该数组的索引0开始复制,“copy,0”指定目标数组copy和从该数组的索引0开始写入数据),math.min(original.length,newLength)计算要复制的元素数量。如果新长度大于原始数组长度,只复制前newLength个元素,如果新长度大于原始数组的长度,则剩余部分用0填充。
3.返回数组copy
/**
* Copies the specified array, truncating or padding with zeros (if necessary)
* so the copy has the specified length. For all indices that are
* valid in both the original array and the copy, the two arrays will
* contain identical values. For any indices that are valid in the
* copy but not the original, the copy will contain <tt>0</tt>.
* Such indices will exist if and only if the specified length
* is greater than that of the original array.
*
* @param original the array to be copied
* @param newLength the length of the copy to be returned
* @return a copy of the original array, truncated or padded with zeros
* to obtain the specified length
* @throws NegativeArraySizeException if <tt>newLength</tt> is negative
* @throws NullPointerException if <tt>original</tt> is null
* @since 1.6
*/
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;
}
Arrays.copyOfRange( )
作用
根据指定范围将原数组内容复制到新数组中,原数组内容不会被修改。是copyOf的升级版,是带范围的复制数组
,相对与copyOf多了一个参数。
复制数组,也可以说是变相的截取数组,因为数组在创建时就定义好长度,只能通过复制操作达到截取的目的,这个方法虽然更加灵活,但需要注意的也不少,源码中只是判断了from不能大于to,但没有排除下标为负数,下标为负数就会报错,且这种错误是native代码中报的,不好捕捉,尽量在执行前先进行参数的校验。
Arrays.copyOfRange( )也有10种重载方法
参数
T[] original:原始数组
int from:开始复制的起始下标(包含)
int to:停止复制的结束下标(不包含)
注:数组下标从0开始
返回值
返回值为数组,即根据用户指定范围复制的数组,新数组的长度为to-from。
举例
String[] 新数组名 = Arrays.copyOfRange(原数组名,复制起始下标,复制截止下标);
“String[] 新数组名”用于存放复制的数组(返回值)
“ Arrays.copyOfRange(原数组名,复制起始下标,复制截止下标)”调用方法
import java.util.Arrays;
public class demo01 {
public static void main(String[] args) {
//原数组
int[] n1 = {1,2,3};
//调用方法
int[] n2 = Arrays.copyOfRange(n1, 2, 5);
//输出原数组
//下标超出原数组,自动填充默认值0
System.out.println(Arrays.toString(n1));
//输出新数组
System.out.println(Arrays.toString(n2));
}
}
运行结果:
import java.util.Arrays;
public class demo01 {
public static void main(String[] args) {
//原数组
int[] n1 = {1,2,3,4,5};
//调用方法
//下标不超出原数组
int[] n2 = Arrays.copyOfRange(n1, 2,4);
//输出原数组
System.out.println(Arrays.toString(n1));
//输出新数组
System.out.println(Arrays.toString(n2));
}
}
运行结果:
源代码解读
1.计算新数组长度。 to和from下标之间的元素数量,如果新长度小于0,不符合使用规则,抛出 from 大于 to 的异常提醒。
2..根据计算出的长度创建了一个数组copy,用来存储我们将复制的数据。
3.调用System.arraycopy(原数组 起始下标,新数组,起始下标,要复制的元素数量)方法将原数组内容复制到新数组里。
其中“original,0”指定原数组和从该数组的索引0开始复制,“copy,0”指定目标数组copy和从该数组的索引0开始写入数据),math.min(original.length,newLength)计算要复制的元素数量。如果新长度大于原始数组长度,只复制前newLength个元素,如果新长度大于原始数组的长度,则剩余部分用0填充。
4.返回数组copy
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;
}
Arrays.fill( )
作用
初始化数组元素为指定值
,即整个数组将会是同一个元素
,也
可以指定区间进行填充
,通过fromIndex和endIndex参数。可以用来快速设置数组中的元素为同一个值。
Arrays.fill()主要分为两种重载方法(不指定范围or指定范围)
参数
Arrays.fill()方法被重载,有两种不同的参数
(1)T[] a:要填充的数组
T val:要填充的值
(2)T[] a:要填充的数组
int fromIndex:开始填充的起始下标(包含)
int toIndex:停止填充的终止下标(不包含)
int val:要填充的值
返回值
没有返回值,作用是直接修改传入的数组
举例
1、不指定范围
import java.util.Arrays;
public class demo01 {
public static void main(String[] args) {
//原数组
int[] n1 = {1,2,3,4,5};
//调用方法
//不指定范围赋值
Arrays.fill(n1,7);
//输出数组
System.out.println(Arrays.toString(n1));
}
}
运行结果:
2、指定范围
import java.util.Arrays;
public class demo01 {
public static void main(String[] args) {
//原数组
int[] n1 = {1,2,3,4,5};
//调用方法
//指定范围赋值
Arrays.fill(n1,2,4,7);
//输出数组
System.out.println(Arrays.toString(n1));
}
}
运行结果:
源代码解读
1、不指定范围(其余类型皆雷同)
通过for循环给数组挨个赋值 int[] a:要填充的数组 int val:要填充的值
public static void fill(int[] a, int val) {
for (int i = 0, len = a.length; i < len; i++)
a[i] = val;
}
2、 指定范围(其余类型皆雷同)
T[] a:要填充的数组 int fromIndex:开始填充的起始下标(包含)
int toIndex:停止填充的终止下标(不包含) int val:要填充的值
1.调用rangeCheck的私有方法,检查fromIndex和toIndex是否在数组a的有效范围内,如果不在有效范围内,则抛出异常
2.通过for循环,从fromIndex开始,递增到toIndex之前的位置,每次迭代都将数组中下标为i的元素赋值为val
private static void rangeCheck(int arrayLength, int fromIndex, int toIndex) {
if (fromIndex > toIndex) {
throw new IllegalArgumentException(
"fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")");
}
if (fromIndex < 0) {
throw new ArrayIndexOutOfBoundsException(fromIndex);
}
if (toIndex > arrayLength) {
throw new ArrayIndexOutOfBoundsException(toIndex);
}
}
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;
}
2.数组转集合
Arrays.asList( )
作用
将一个数组转换为一个固定大小的List列表。
这是开发中常用的初始化数组
手段,比给List中一个一个add元素更快捷,不过此方法返回的List是java.util.Arrays.ArrayList
类型,不支持增加、删除元素
,但是可以修改和查看
,一般用于初始化一个静态集合
,只是用于读取和查看,相当于常量。
参数
asList(T... a) ,是一个可变参数可以传递任意数量的对象作为参数,这些对象将被视为一个数组的元素。
返回值
List<T>,返回一个固定大小的List,包含了传递给Arrays.asList()的所有元素。不能添加或删除元素。
举例
import java.util.Arrays;
import java.util.List;
public class demo01 {
public static void main(String[] args) {
//原数组
Integer[] n1 = {1,2,3,4,5};
//调用方法
//数组-->集合
//此方法不支持基础类型 只支持引用类型
List<Integer> n2 = Arrays.asList(n1);
//输出集合
System.out.println(n2);
}
}
输出结果:
将这一数组转换为List集合后,对应的列表是不支持添加和删除操作的,否则会报错
import java.util.Arrays;
import java.util.List;
public class demo01 {
public static void main(String[] args) {
//原数组
Integer[] n1 = {1,2,3,4,5};
//调用方法
//数组-->集合
//此方法不支持基础类型 只支持引用类型
List<Integer> n2 = Arrays.asList(n1);
//输出集合
System.out.println(n2);
//添加一个元素5
n2.add(5);
}
}
输出结果:
转变为集合后,对集合进行修改,数组的元素也会修改
import java.util.Arrays;
import java.util.List;
public class demo01 {
public static void main(String[] args) {
//原数组
Integer[] n1 = {1,2,3,4,5};
//调用方法
//数组-->集合
//此方法不支持基础类型 只支持引用类型
List<Integer> n2 = Arrays.asList(n1);
//输出数组
System.out.println(n2);
//添加一个元素5
n2.set(2, 4);
//输出集合
System.out.println(n2);
//输出数组
System.out.println(Arrays.toString(n1));
}
}
运行结果:
源代码解读
这个方法接受一个泛型参数 T,可以处理任何类型的对象,只要调用时提供一致的类型即可,必须是一个引用类型,不能是一个基本类型。
@SafeVarargs
@SuppressWarnings("varargs")
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
Arrays.asList() 返回的 List 是一个 Arrays 类的内部类,它持有一个对原始数组的引用。这意味着对 List 的修改会反映到数组上。
3.数组排序
Arrays.sort( )
作用
对数组进行升序排序(快速排序)。
参数
Arrays.sort()方法被重载,主要分为两种
1.无范围排序: Object[] a :要排序的数组
2.范围排序: Object[] a :要排序的数组
int fromIndex:起始下标 int toIndex:截止下标
返回值
没有返回值,直接修改传入的数组,将其排序。
举例
import java.util.Arrays;
public class xx {
public static void main(String[] args) {
Integer[] n1 = {5,7,9,1,5,4,8,6,3,2};
//输出排序前数组
System.out.println(Arrays.toString(n1));
//无范围排序
Arrays.sort(n1);
//输出排序后数组
System.out.println(Arrays.toString(n1));
System.out.println();
Integer[] n2 = {5,7,9,1,5,4,8,6,3,2};
//输出排序前数组
System.out.println(Arrays.toString(n2));
//范围排序
Arrays.sort(n2, 3, 7);
//输出排序后数组
System.out.println(Arrays.toString(n2));
}
}
运行结果:
源代码解读
对对象数组进行排序。它接受两个参数:一个对象数组 a和一个 Comparator 对象 c。Comparator 用于定义数组中元素的排序顺序。r如果Comparator对象c不是null,则进入else代码块,检查LegacyMergeSort.userRequested的静态变量,这个变量可能是一个标志,指示是否应该使用传统的归并排序算法,如果LegacyMergeSort.userRequested为true,则调用legacyMergeSort(a,c)方法对数组a使用归并排序算法进行排序,并使用提供的Comparator c.如果为false,则调用Time.sort(a,0,a.length,c,null,0,0)方法对数组a使用TimeSort算法进行排序。
public static void sort(Object[] a) {
if (LegacyMergeSort.userRequested)
legacyMergeSort(a);
else
ComparableTimSort.sort(a, 0, a.length, null, 0, 0);
}
public static void sort(Object[] a, int fromIndex, int toIndex) {
rangeCheck(a.length, fromIndex, toIndex);
if (LegacyMergeSort.userRequested)
legacyMergeSort(a, fromIndex, toIndex);
else
ComparableTimSort.sort(a, fromIndex, toIndex, null, 0, 0);
}
4.数组查询
Arrays.binarySearch( )
作用
在排好序的数组中查找指定元素,并返回下标。如果找到该元素,则返回其下标(从0 开始),如果未找到该元素,则返回负值。
参数
Arrays.binarySearch()方法被重载,主要有两种不同的参数
(1)、binarySearch(T[] a,T key):
T[] a:已排序的数组 T key): 目标查找元素
(2)、binarySearch(T[] a,int fromIndex,int toIndex,T key):
T[] a:已排序的数组 T key: 目标查找元素
int fromIndex:从此下标开始查找(包括)
int toIndex:到此下标停止查找(不包括)
返回值
如果找到目标元素,则返回该元素在数组中的下标(从0开始),如果没找到,则返回负值。
举例
1、无范围查找元素
import java.util.Arrays;
public class demo01 {
public static void main(String[] args) {
Integer[] n1 = {5,7,9,1,4,8,6,3,2};
//排序
Arrays.sort(n1);
//输出数组
System.out.println(Arrays.toString(n1));
//查找
int index = Arrays.binarySearch(n1, 5);
//输出下标
System.out.println(index);
}
}
运行结果:
2、指定范围查找元素
import java.util.Arrays;
public class demo01 {
public static void main(String[] args) {
Integer[] n1 = {5,7,9,1,5,4,8,6,3,2};
//排序
Arrays.sort(n1);
//输出数组
System.out.println(Arrays.toString(n1));
//查找
int index = Arrays.binarySearch(n1,4,8,5);
//输出下标
System.out.println(index);
}
}
运行结果:
注:如数组中由相同元素,则返回最后一个目标元素的下标
源代码解读
调用binarySearch0()的私有方法,执行二分搜索逻辑,根据传入的起始和结束下标(无范围查找调用binarySearch0()时自动传入0,数组长度), 以及要查找的键,来在数组中搜素元素并返回目标值的下标。
二分查找:当一个数组经过排序后,先寻找中间的数据进行比较,当即排除一半数据,依此类推,减少寻址次数,相当于指数倍缩减。
无范围查找
binarySearch0( )方法实现思路:low为低位0,high为高位数组a.length-1,然后通过计算得到中间位置mid,获取中间值midVal,然后再用中间值midVal和目标值key相较。
若中间值midVal大于目标值key时,说明目标元素可能在目标数组的上半区,将high=mid-1,然后继续计算中间值,依次循环,直到low<=high时,循环结束。若找到目标元素,则返回其下标,若没找到,则返回一个负数 -(low + 1)
若中间值midVal小于目标值key时,说明目标元素可能在目标数组的下半区,将low=mid+1,然后继续计算中间值,依次循环,直到low<=high时,循环结束。若找到目标元素,则返回其下标,若没找到,则返回一个负数 -(low + 1)
public static int binarySearch(Object[] a, Object key) {
return binarySearch0(a, 0, a.length, key);
}
指定范围查找
public static int binarySearch(Object[] a, int fromIndex, int toIndex,
Object key) {
rangeCheck(a.length, fromIndex, toIndex);
return binarySearch0(a, fromIndex, toIndex, key);
}
引用
// Like public version, but without range checks.
private static int binarySearch0(Object[] a, int fromIndex, int toIndex,
Object key) {
int low = fromIndex;
int high = toIndex - 1;
while (low <= high) {
int mid = (low + high) >>> 1;
@SuppressWarnings("rawtypes")
Comparable midVal = (Comparable)a[mid];
@SuppressWarnings("unchecked")
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 void rangeCheck(int arrayLength, int fromIndex, int toIndex) {
if (fromIndex > toIndex) {
throw new IllegalArgumentException(
"fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")");
}
if (fromIndex < 0) {
throw new ArrayIndexOutOfBoundsException(fromIndex);
}
if (toIndex > arrayLength) {
throw new ArrayIndexOutOfBoundsException(toIndex);
}
}
5.数组打印
Arrays.toString( )
作用
将数组(无论是基本类型数组还是对象数组)的内容转换为一个格式化字符串,并且整个数组被包含在方括号[] 中。
参数
int[] a:基本类型数组,将基本类型数组转换为它的字符串表示的形式。
Object[] a:对象数组,调用每个对象的toString()方法来获取其字符串表示形式。
返回值
字符串
举例
import java.util.Arrays;
public class xx {
public static void main(String[] args) {
Integer[] n1 = {5,7,9,1,5,4,8,6,3,2};
System.out.println(Arrays.toString(n1));
}
}
运行结果:
源代码解读
创建一个StringBuilder对象,用于构建最终的字符串表示,提高字符串拼接的效率。向StringBuilder对象b中追加一个'[',作为数组字符串的开头,for循环,遍历数组并追加每个元素,将当前索引 i 对应的数组元素追加到 StringBuilder 中,检查当前索引i是否已经是数组的最后一个索引,如果是数组的最后一个元素,则追加一个右方括号 ']',并将 StringBuilder 对象转换为字符串表示,然后返回这个字符串。toString 方法会返回一个形如 [元素1, 元素2, ..., 元素n] 的字符串,其中 元素1 到 元素n 是数组中的各个元素。如果数组为空,则返回 [];如果数组为 null,则返回 "null"。
public static String toString(Object[] 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(String.valueOf(a[i]));
if (i == iMax)
return b.append(']').toString();
b.append(", ");
}
}
Arrays.deeptoString( )
作用
打印输出多维数组
参数
Object[] a:目标数组
返回值
字符串
举例
import java.util.Arrays;
public class xx {
public static void main(String[] args) {
int n1[][]= {
{5,7,9},
{1,5,4},
{8,6,3}
};
System.out.println(Arrays.deepToString(n1));
}
}
运行结果:
源代码解读
使用StringBuilder进行了元素的拼接,之后调用toString方法打印到控制台,只不过需要注意的是,如果是对象数组,需要重写对象的toString方法,不然打印的还是对象的内存地址。
public static String deepToString(Object[] a) {
if (a == null)
return "null";
int bufLen = 20 * a.length;
if (a.length != 0 && bufLen <= 0)
bufLen = Integer.MAX_VALUE;
StringBuilder buf = new StringBuilder(bufLen);
deepToString(a, buf, new HashSet<Object[]>());
return buf.toString();
}
private static void deepToString(Object[] a, StringBuilder buf,
Set<Object[]> dejaVu) {
if (a == null) {
buf.append("null");
return;
}
int iMax = a.length - 1;
if (iMax == -1) {
buf.append("[]");
return;
}
dejaVu.add(a);
buf.append('[');
for (int i = 0; ; i++) {
Object element = a[i];
if (element == null) {
buf.append("null");
} else {
Class<?> eClass = element.getClass();
if (eClass.isArray()) {
if (eClass == byte[].class)
buf.append(toString((byte[]) element));
else if (eClass == short[].class)
buf.append(toString((short[]) element));
else if (eClass == int[].class)
buf.append(toString((int[]) element));
else if (eClass == long[].class)
buf.append(toString((long[]) element));
else if (eClass == char[].class)
buf.append(toString((char[]) element));
else if (eClass == float[].class)
buf.append(toString((float[]) element));
else if (eClass == double[].class)
buf.append(toString((double[]) element));
else if (eClass == boolean[].class)
buf.append(toString((boolean[]) element));
else { // element is an array of object references
if (dejaVu.contains(element))
buf.append("[...]");
else
deepToString((Object[])element, buf, dejaVu);
}
} else { // element is non-null and not an array
buf.append(element.toString());
}
}
if (i == iMax)
break;
buf.append(", ");
}
buf.append(']');
dejaVu.remove(a);
}