参考笔记:
Java中Arrays类(操作数组的工具)_java arrays-CSDN博客
Java——Arrays 类详解_java arrays类-CSDN博客
目录
1.Arrays类简介
① Arrays 类是 Java 语言中用于操作数组的一个工具类,提供了多种静态方法来处理数组,包括排序、搜索、填充、比较等操作
② Arrays 类位于 java.util.Arrays 中,该类所有的构造方法被设置为 private ,因此无法创建 Arrays 对象
③ Arrays 所有方法都是静态的,可以直接通过 "Arrays.方法" 调用
2.Arrays.toString
Arrays.toString(...):返回任何类型数组的字符串形式
2.1 使用示例
import java.util.Arrays;
public class demo {
public static void main(String[] args) {
int[] arr = {1,2,3};
System.out.println(Arrays.toString(arr));
}
}
运行结果:
2.2 源码
底层是重载了多个 toString 方法,这里以 String toString(int[ ] a) 为例
public static String toString(int[] a) {
if (a == null){//判断数组a是否为空,为空则返回字符串"null"
return "null";
}
int iMax = a.length - 1;//数组a的最后一个元素索引
if (iMax == -1){//如果iMax == -1,说明数组a的长度为0
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(", ");
}
}
3. Arrays.copyOf
Arrays.copyOf(type[ ] original,int newLength):拷贝任意类型数组,返回值是要拷贝的那个数组类型
original: 原数组
newLength: 指定新数组的长度
补充:也可以理解为从原数组 original 中复制 newLength 个元素到新数组中,如果 newLength 大于原数组的大小,则新数组中大于原数组大小的部分的元素都是类型默认值
3.1 使用示例
import java.util.Arrays;
public class demo {
public static void main(String[] args) {
int[] arr1 = {1,2,3,4};
int arr1_len = arr1.length;
int[] tmp1 = Arrays.copyOf(arr1,arr1_len); //指定tmp1数组的长度为arr1_len,并拷贝array数组并赋值到tmp1数组上
System.out.println(tmp1.length);
System.out.println(Arrays.toString(tmp1)); //打印tmp1数组,[1,2,3,4]
System.out.println("-----------------------------");
int[] arr2 = {4,5,6,7};
int arr2_len = arr2.length;
int[] tmp2 = Arrays.copyOf(arr2,2 * arr2_len); //指定tmp2数组的长度为2 * arr2_len,并拷贝array数组并赋值到tmp2数组上,
System.out.println(tmp2.length);
System.out.println(Arrays.toString(tmp2)); //打印tmp2数组,[4,5,6,7,0,0,0,0]
System.out.println("-----------------------------");
int[] arr3 = {9,10,11,12};
int[] tmp3 = Arrays.copyOf(arr3,2);//指定tmp3数组的长度为2,则只会拷贝array数组的前2个元素到tmp3数组上
System.out.println(tmp3.length);
System.out.println(Arrays.toString(tmp3));//打印tmp3数组,[9,10]
}
}
3.2 源码
4.Arrays.sort
注:学 Arrays.sort 之前,最好先学一下关于 Comparable、Comparator 接口,可以看到我写的另外一篇博客:【Java SE】Java比较器:Comparable、Comparator-CSDN博客
默认排序规则:
String:按照字符串中字符的 ASCII 值进行比较
Character:按照字符的 ASCII 值来进行比较
数值类型对应的包装类以及 BigInteger、BigDecimal :按照它们对应的数值大小进行比较
Boolean:true 对应的包装类实例大于 false 对应的包装类实例
Date、Time等:后面的日期时间比前面的日期时间大
① Arrays.sort(type[ ] a):对整个 a 数组进行默认排序
② Arrays.sort(type[ ] a,int fromIndex,int toIndex):对 a 数组索引在 [fromIndex,toIndex) 的元素进行默认排序
③ Arrays.sort( T[ ] a,Comparator <? super T> c):指定比较器 Comparator,对整个 a 数组进行定制排序
④ Arrays.sort( T[ ] a,int fromIndex,int toIndex,Comparator <? super T> c):指定比较器 Comparator,对 a 数组索引在 [fromIndex,toIndex) 的元素进行定制排序
4.1 默认排序使用示例
import java.util.Arrays;
public class demo {
public static void main(String[] args) {
//1:Arrays.sort(type[ ] a):
int[] arr1 = {-1,10,300,4};
Arrays.sort(arr1);//从小到大排序
System.out.println(Arrays.toString(arr1));//[-1,4,10,300]
//2:Arrays.sort(type[ ] a,int fromIndex,int toIndex):
int[] arr2 = {100,10,5,1};
Arrays.sort(arr2,0,2);
System.out.println(Arrays.toString(arr2));//[10,100,5,1]
}
}
4.2 定制排序使用实例
import java.util.Arrays;
import java.util.Comparator;
public class demo {
public static void main(String[] args) {
//方式1.使用匿名内部类传入比较器 Comparator 接口
Integer[] arr = {-1,100,4444,23};
System.out.println("arr降序排序前:"+Arrays.toString(arr));
//对arr实现降序排序
Arrays.sort(arr, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
if(o1 > o2){
return -1;//返回任何小于0的整数都可以
}
return 1;//返回任何大于0的整数都可以
}
});
System.out.println("arr降序排序后:"+Arrays.toString(arr));//[4444,100,23,-1]
System.out.println("--------------------------------");
//方式2.定制比较器类实现 Comparator 接口
StudentAgeComparator sc = new StudentAgeComparator();
Student s1 = new Student("小马", 55);
Student s2 = new Student("蔡徐坤", 24);
Student s3 = new Student("ftt", 17);
Student s4 = new Student("jack", 30);
Student[] personList = {s1,s2,s3,s4};
System.out.println("personList按年龄降序排序前:");
Arrays.stream(personList).forEach(student -> System.out.println(student));
//对personList数组按对象的年龄降序排序
Arrays.sort(personList,sc);
System.out.println("personList按年龄降序排序后:");
Arrays.stream(personList).forEach(student -> System.out.println(student));
}
}
//定制比较器类,按学生的年龄降序排序
class StudentAgeComparator implements Comparator<Student>{
@Override
public int compare(Student s1, Student s2) {
if (s1.age > s2.age){
return -1;
}else if (s1.age < s2.age){
return 1;
}else {
return 0;
}
}
}
class Student{
public String name;
public int age;
public Student(){}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
运行结果
Debug追溯
这里以上面的代码 Debug 一下看看 Arrays.sort 底层是如何用到我们指定的比较器 Comparator 来进行排序的,如下 GIF 所示:
可以看到,Arrays.sort 底层最终会调用 TimSort 类的 binarySort 方法,该方法中,
if (c.compare(pivot, a[mid]) < 0)
right = mid;
c 即为我们传入的比较器 StudentAgeComparator
4.3 Arrays.sort使用的算法
① 基本类型数组(如 int[ ], char[ ] 等):
对于基本数据类型数组,Arrays.sort( ) 方法使用了一种名为 Dual-Pivot Quicksort 的排序算法这是一种改进的快速排序算法,由 Vladimir Yaroslavskiy 提出。它在平均情况下具有 O(n log n) 的时间复杂度,在实践中通常表现良好
② 引用类型数组(如 Integer[ ], String[ ] 等):
对于引用类型数组,Arrays.sort( ) 方法则使用 Timsort 算法。这是一种混合排序算法,结合了归并排序和插入排序的优点。Timsort 是为了优化对部分有序数据的排序而设计的,具有 O(n log n) 的时间复杂度,在实际应用中表现出色
5.Arrays.binarySearch
Arrays.binarySearch为二分查找算法。在已排序的数组中查找指定的某个元素,返回在数组中的索引
① Arrays.binarySearch(type[ ] a,type key):在整个数组 a 中查找元素 key,返回其索引。未找到时返回的是一个特定的负数,为 - (low + 1)
② Arrays.binarySearch(type[ ] a,int fromIndex,int toIndex,type key):在数组 a 索引为[fromIndex,toIndex) 的元素中查找元素 key,返回其索引
5.1 使用示例
import java.util.Arrays;
public class demo {
public static void main(String[] args) {
int[] arr1 = {11, 22, 33, 44};
int index1 = Arrays.binarySearch(arr1, 44);
System.out.println("index of 44: " + index1);//3
int[] arr2 = {11, 22, 33, 44};
int index2 = Arrays.binarySearch(arr2,0,1,44);
System.out.println("index of 44: " + index2);//返回的是一个负数,在[0,1)区间没找到44
}
}
5.2 源码
流程图
binarySearch0源码
binarySearch0 就是传统的二分查找算法,比较简单
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;//相当于mid = (low + high) / 2
int midVal = a[mid];//中间元素值
if (midVal < key)
low = mid + 1;
else if (midVal > key)
high = mid - 1;
else
return mid;
}
return -(low + 1); // 没有找到不是直接返回负一,返回一个特定的负数
}
6.Arrays.fill
① Arrays.fill (type[ ] a,type val):将 a 数组中的所有元素赋值为 val
② Arrays.fill (type[ ] a,int fromIndex,int toIndex,type val):将 a 数组中索引为 [fromIndex,toIndex) 的元素赋值为 val
6.1 使用示例
import java.util.Arrays;
public class demo {
public static void main(String[] args) {
//1:Arrays.fill (type[ ] a,type val):将a数组中的所有元素赋值为val
int[] arr = new int[10];
Arrays.fill(arr, 5);
Arrays.stream(arr).forEach(i -> System.out.print(i + " "));
System.out.println();
System.out.println("-------------------");
//2:Arrays.fill (type[ ] a,int fromIndex,int toIndex,type val):将a数组中索引为[fromIndex,toIndex)的元素赋值为val
Arrays.fill(arr,0,5,4);
Arrays.stream(arr).forEach(i -> System.out.print(i + " "));
}
}
6.2 源码
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;
}
7. Arrays.equals
Arrays.equals (type[ ] a,type[ ] a2):返回值为 boolean ;判断一维数组 a 与 一维数组 a2 的内容是否完全相同,两个数组的 type 需要一致
7.1 使用案例
import java.util.Arrays;
public class demo {
public static void main(String[] args) {
int[] arr1 = {1,2,3,4};
int[] arr2 = {1,2,3,4};
int[] arr3 = {100,99};
System.out.println(Arrays.equals(arr1, arr2));//true
System.out.println(Arrays.equals(arr1, arr3));//false
}
}
7.2 源码
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++)//逐个比较
if (a[i] != a2[i])
return false;
return true;
}
8. Arrays.deepEquals
Arrays.deepEquals(Object[ ] a1,Object[ ] a2) :用于比较两个多维数组 a1、a2 的内容是否完全相同
使用案例
import java.util.Arrays;
public class demo {
public static void main(String[] args) {
int[][] array1 = {
{1, 2, 3}, {4, 5, 6}};
int[][] array2 = {
{1, 2, 3}, {4, 5, 6}};
int[][] array3 = {
{1, 2, 3}, {4, 5, 7}};
// 比较相同的二维数组
boolean isEqual1 = Arrays.deepEquals(array1, array2);
System.out.println("array1 和 array2 相等? " + isEqual1); // 输出: true
// 比较不同的二维数组
boolean isEqual2 = Arrays.deepEquals(array1, array3);
System.out.println("array1 和 array3 相等? " + isEqual2); // 输出: false
}
}
9.Arrays.asList
Arrays.asList( ) 方法是 Arrays 类的一个静态方法。它可以将传入的数据转换为一个固定大小的列表 List
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
9.1 使用细节和案例
① 形参是 (T.. a) 表示可以接收 0 个或者多个类型为 T(String、八大包装类型、自定义类型) 的参数,编译器会将传入的实参转换为T[ ] a 数组,当然也可以直接传入一个 T[ ] 数组
② 返回的 List 的运行类型是 java.util.Arrays.ArrayList,是 Arrays 类的一个内部类,并不是平时经常使用的 java.util.ArrayList。这里要注意区分,很容易弄混
验证
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class demo {
public static void main(String[] args) {
//1:传入多个数据
List<String> list1 = Arrays.asList("小马","唱跳rap篮球");
//2:直接传入数组
Integer[] arr1 = {1,2,3};
List<Integer> list2 = Arrays.asList(arr1);
System.out.println("list1的运行类型:" + list1.getClass());
System.out.println("list2的运行类型:" + list2.getClass());
//这里new的是java.util.ArrayList
List<Object> list3 = new ArrayList<>();
System.out.println("list3的运行类型:" + list3.getClass());
}
}
运行结果:
③ 返回的 List 不是一个全新的独立集合,而是对原始数组的包装视图:Wrapper View ,具体来说:
-
共享存储空间:
-
返回的 List 直接引用原数组的内存地址
-
修改 List 中的元素 = 直接修改数组对应位置的元素,反之亦然
-
-
固定大小特性
-
由于数组长度不可变,因此返回的 List 会禁止结构性修改,比如不能使用 add 和 remove 来增加或删除元素,这与 java.util.ArrayList 是完全不一样的
-
验证:共享存储空间
import java.util.Arrays;
import java.util.List;
public class demo {
public static void main(String[] args) {
Integer[] arr = {1, 2, 3, 4, 5};
//转换为列表
List<Integer> list = Arrays.asList(arr);
//修改列表中的元素
list.set(0, 10);
//打印修改后的列表和数组
System.out.println("执行:list.set(0,10)");
System.out.println("执行后的列表list: " + list); // 输出: 修改后的列表: [10, 2, 3, 4, 5]
System.out.println("执行后的数组arr: " + Arrays.toString(arr)); // 输出: 修改后的数组: [10, 2, 3, 4, 5]
System.out.println("------------------------------------------------");
//修改数组中的元素
arr[1] = 999;
//打印修改后的列表和数组
System.out.println("执行:arr[1] = 999");
System.out.println("执行的列表list: " + list); // 输出: 修改后的列表: [10, 999, 3, 4, 5]
System.out.println("执行后的数组arr: " + Arrays.toString(arr)); // 输出: 修改后的数组: [10, 999, 3, 4, 5]
}
}
运行结果:
验证:固定大小特性,即不能使用 add 和 remove,编译可以通过,但会报异常
从控制台的异常信息可以看到,调用的是父类 AbstractList .add 方法,这里溯源一下,如下图:
父类 AbstractList 的 add 方法只是简单的 throw 一个异常,因此在使用 add 方法时会发生异常。 remove 方法也是类似,这里就不再赘述了