Java基础知识16--Arrays、System常用方法使用
1.Arrays概述
Arrays类位于 java.util 包中,主要包含了操作数组的各种方法。
1.1 toString方法
一维数组转字符串
public static String toString(int[] a)
案例:
int[] array1 = new int[]{1, 2, 3}; int[] array2={6,7,8,9}; System.out.println(Arrays.toString(array1)); //[1, 2, 3] System.out.println(Arrays.toString(array2)); //[6, 7, 8, 9]
1.2 deepToString方法
如果是一维数组,toString方法可以很好的适用。但遇到多维数组时,需要使用deepToString把数组完全转成字符串。
public static String deepToString(Object[] a)
案例:
int[][] deepArray = new int[][]{{1, 3},{2, 4}}; System.out.println(Arrays.deepToString(deepArray)); //[[1, 3], [2, 4]]
1.3 fill方法
填充数组
将指定的int值分配给指定的int数组的指定范围的每个元素。 要填充的范围从索引fromIndex扩展到索引toIndex
public static void fill(int[] a,int fromIndex,int toIndex,int val)
案例:
int[] array1 = new int[5]; Arrays.fill(array1, 2); System.out.println(Arrays.toString(array1)); //[2, 2, 2, 2, 2] int[] array2 = new int[5]; Arrays.fill(array2, 1, 4, 2); //部分填充 System.out.println(Arrays.toString(array2));//[0, 2, 2, 2, 0]
1.4 sort方法
1.4.1 数组元素排序
public static void main(String[] args) { int[] array1 = new int[]{3, 10, 4, 0, 2}; Arrays.sort(array1); System.out.println(Arrays.toString(array1)); //[0, 2, 3, 4, 10] int[] array2 = new int[]{3, 10, 4, 0, 2}; Arrays.parallelSort(array2); //和sort相比是这个是并行的 System.out.println(Arrays.toString(array2)); //[0, 2, 3, 4, 10] int[] array3 = new int[]{3, 10, 4, 0, 2}; Arrays.sort(array3, 0, 4); //部分排序 System.out.println(Arrays.toString(array3)); //[0, 3, 4, 10, 2] }
1.4.2 对其他对象数组进行排序
一个对象数组,排序算法需要重复比较数组中的元素。不同的类比较元素的规则是不同的,但是排序算法只应该调用类提供的比较方法,只要所有的类就比较的时候提供的方法达成一致,那么排序算法就能开始工作。这个在排序时对象之间进行比较方法就可以是一个接口,所有需要比较的对象继承这个接口并且实现比较的方法,就可以对这些对象进行排序。
如果一个类想启用对象排序,那么就应该实现Comparable接口。
public static void main(String[] args){ Employee[] employees = new Employee[3]; employees[0] = new Employee(20); employees[1] = new Employee(10); employees[2] = new Employee(30); Arrays.sort(employees); for(Employee e : employees){ System.out.println(e); //Employee{id=10} Employee{id=20} Employee{id=30} } } static class Employee implements Comparable{ private int id; public Employee(int id){this.id = id;} @Override public int compareTo(Employee o) { return this.id - o.id; } @Override public String toString() { return "Employee{" + "id=" + id + '}'; } }
1.4.3 自定义排序规则
String[] names = {"tom", "alice", "fred"}; Arrays.sort(names); System.out.println(Arrays.toString(names));//[alice, fred, tom]
假如想根据字符串的长度而不是根据字典顺序对字符串排序,但是String类我们是无法修改的。上面的代码对String数组进行排序,只能按照字典顺序对String数组进行排序。
Arrays.sort方法和Collections.sort方法都提供了一个可以接收Comparator实例作为第二个参数的版本。
要按照长度比较字符串,定义一个实现Comparator
的类。
public static void main(String[] args){ String[] names = {"tom", "alice", "fred"}; Arrays.sort(names, new LengthComparator()); System.out.println(Arrays.toString(names)); //[tom, fred, alice] } public static class LengthComparator implements Comparator{ @Override public int compare(String o1, String o2) { return o1.length() - o2.length(); // 返回值<0,对应false,则o1与o2的顺序不交换;返回值>0,对应true,则o1与o2的顺序交换 } }
像Comparator、Runable等这=一些接口有一个特点就是只有一个抽象方法(其他的都是static或者default的方法),比如继承Comparator接口只需要重写compare方法,继承Runnable接口只需要重写run方法,这种类型的接口被称为函数式接口,可以被lambda表达式所代替。
比如上面根据字符串的长度进行排序的代码,Arrays.sort的第二个参数是需要实现了Comparator接口的实例,用lambda表达是就可以写成这样:
public static void main(String[] args){ String[] names = {"tom", "alice", "fred"}; Comparatorcomp = (first, second) -> {return first.length() - second.length();}; /*Comparator comp = new Comparator */ Arrays.sort(names, comp); System.out.println(Arrays.toString(names)); }() { @Override public int compare(String first, String second) { return first.length() - second.length(); } };
1.5 equals与deepEquals
数组的比较
deepEquals用于判定两个指定数组彼此是否深层相等,此方法适用于任意深度的嵌套数组。
equals用于判定两个数组是否相等,如果两个数组以相同顺序包含相同元素,则返回true。
public static boolean equals(int[] a,int[] a2)
public static boolean deepEquals(Object[] a1,Object[] a2)
案例:
int[] array = new int[]{1, 2, 3}; int[] array2 = new int[]{1, 2, 3}; System.out.println(Arrays.equals(array, array2)); //true int[][] deepArray1 = new int[][]{{1, 3},{2, 4}}; int[][] deepArray2 = new int[][]{{1, 3},{2, 4}}; System.out.println(Arrays.equals(deepArray1, deepArray2)); //false System.out.println(Arrays.deepEquals(deepArray1, deepArray2)); //true
1.6 asList方法
将数组转化成List集合
1.6.1 源码分析
Arrays.asList()所作操作:
@SafeVarargs public staticList asList(T... var0) { return new Arrays.ArrayList(var0); }
看上去是个很正常的方法,然而实际上你点进到 ArrayList
发现,其实 ArrayList
并不是我们平时用的 ArrayList,而是
Arrays
里面的一个内部类。
而且这个内部类没有 add,clear,remove
方法(即没有重写实现这些方法),所以抛出的异常其实来自于AbstractList
。
public void add(int index, E element) { throw new UnsupportedOperationException(); } public E remove(int index) { throw new UnsupportedOperationException(); }
Arrays.asList()不是坑 设计的初衷就是要生成一个不可更改的list,保证数据的安全性的场景,比如要生成一个常量list,里面的值都是约定好了的,不能被代码随意修改,这种就很需要这个方法,就算jdk不提供,需要用到的也是会自己生成一个内部类去实现这个的。
总结:
- Arrays.asList() 不要乱用,底层其实还是数组。ArrayList底层是Object[]数组。
- 如果使用了 Arrays.asList() 的话,最好不要使用其集合的操作方法。
- List list = new ArrayList<>(Arrays.asList("a", "b", "c")) 可以在外面这样包一层真正的 ArrayList。
注意:
(1)该方法适用于对象型数据的数组(String、Integer...)
(2)该方法不建议使用于基本数据类型的数组(byte,short,int,long,float,double,boolean)
(3)该方法将数组与List列表链接起来:当更新其一个时,另一个自动更新
(4)不支持add()、remove()、clear()等方法
1.6.2 使用案例
(1)对象类型(String型)的数组数组使用asList()
String[] strings = {"aa", "bb", "cc"}; ListstringList = Arrays.asList(strings); for(String str : stringList){ System.out.print(str + " "); //控制台输出:aa bb cc }
(2)对象类型(Integer)的数组使用asList()
Integer[] ints = new Integer[] {1, 2, 3}; ListintList = Arrays.asList(ints); for(int i : intList){ System.out.print(i + " "); //控制台输出:1 2 3 }
debug模式查看:
(3)基本数据类型的数组使用asList()
public static void main(String[] args){ int[] ints = new int[]{1, 2, 3}; List<int[]> intList = Arrays.asList(ints); System.out.print("3、基本数据类型的数组使用asList(),出错(输出的是一个引用,把ints当成一个元素了):"); for(Object o : intList){ System.out.print(o.toString());//基本数据类型的数组使用asList(),出错(输出的是一个引用,把ints当成一个元素了):[I@37bba400 } }
注意:基本数据类型int的数组调用asList之后得到的List只有一个元素,这个元素就是元素类型的数组。而封装类Integer数组调用asList是把数组中每个元素加到了List中。
(4)当更新数组或者List,另一个将自动获得更新
public static void main(String[] args){ Integer[] ints = new Integer[]{1, 2, 3}; ListintList = Arrays.asList(ints); for (Integer integer : intList) { System.out.print(integer+" "); //控制台输出:1 2 3 } ints[0]=100; System.out.println(); for (Integer integer : intList) { System.out.print(integer+" "); //控制台输出:100 2 3 } }
(5)获取真正的 ArrayList
public static void main(String[] args){ Integer[] ints = new Integer[]{1, 2, 3}; ListintList = new ArrayList<>(Arrays.asList(ints)); ints[0]=100; for (Integer integer : intList) { System.out.print(integer+" "); } System.out.println(); System.out.println(Arrays.toString(ints)); }
通过debug模式查看可知:
1.7 copyOf和copyOfRange
1.7.1 copyOf方法
复制指定的数组,用零截取或填充(如有必要),以便复制具有指定的长度。
public static int[] copyOf(int[] original,int newLength)
案例:
int []arr = {10,20,30,40,50}; int []arr1 = Arrays.copyOf(arr, 3); System.out.println(Arrays.toString(arr1)); //[10, 20, 30] System.out.println(Arrays.toString(arr)); //[10, 20, 30, 40, 50]
注意:copyOf底层调用的是System.arraycopy()方法实现复制。
System中提供了一个native静态方法arraycopy(),可以使用这个方法来实现数组之间的复制。对于一维数组来说,这种复制属性值传递,修改副本不会影响原来的值。对于二维或者一维数组中存放的是对象时,复制结果是一维的引用变量传递给副本的一维数组,修改副本时,会影响原来的数组。
native 方法不是用Java语言写的,一般都是出于性能考虑,或者用c++,来操作Java不能直接操作的系统资源,因此,System.arraycopy()具有更好的性能。
c语言有memcpy(void *destin, void *source, unsigned n)方法
,函数的功能是从源内存地址的起始位置开始拷贝若干个字节到目标内存地址中,性能很优秀。
参考文献:https://blog.csdn.net/zhzh402/article/details/79670509
https://blog.csdn.net/Mrs_chens/article/details/103726297
System.arraycopy 参考文献:https://blog.csdn.net/qq_32440951/article/details/78357325