被忽视的数组
标签: Java编程思想
目录:
数组究竟是什么
首先我们要谈的是:数组究竟是什么?
基本的看法:
对数组的基本看法是:相同类型的,通过[]标识符来定义引用,并且通过new关键字创建,并需要分配空间来使用的一个对象;通过整形索引值访问它的元素,并且它的尺寸是不可变的。
那么数组有什么地方值得我们去研究的呢?那就是数组作为对象的特殊性。
数组的类是什么?
首先我们知道,在Java中,数组是一个对象。这是毋庸置疑的。那么我们有没有想过:数组作为一个对象,它的类是什么呢?
是java.util.Arrays
吗?不是,Arrays类包含用来操作数组(比如排序和搜索)的各种方法,像是一个工具类。
是java.lang.reflect.Array
吗?也不是,Array类提供了动态创建和访问 Java 数组的方法。这是一个反射工具包,全是静态方法。我们要注意是动态创建数组,也就是说可以调用Array.newInstance(Class<?> componentType,int length)
方法来创建一个数组,这其中设计到反射的知识:
Code:
import java.lang.reflect.Array;
import java.util.Arrays;
/**
* Created by japson on 7/27/2017.
*/
public class TestArray1 {
public static void main(String[] args) throws ClassNotFoundException {
Class<?> clazz = Class.forName("java.lang.Integer");
Object arr = Array.newInstance(clazz,5);
for(int i = 0;i < 5;i++) {
Array.set(arr,i,100);
}
for(int i = 0;i < 5;i++) {
System.out.println(Array.get(arr,i));
}
}
}
Analysis:
在以上代码中:
- 我们首先创建了Class类型的对象clazz,并将其指向Integer类;
- 创建了一个Object类型的对象arr,指向Array调用newInstance方法,创建一个具有指定的组件类型和维度的新数组。
- 通过for循环和Array.set(Object array,int index,Object value)方法给arr数组赋值。
- 循环输出arr数组,通过Array.get(Object array,int index)
虽然通过java.lang.reflect.Array
也能动态的创建,但是很明显不是我们常见的方法,因为Array不是数组的类,它智能动态的创建和操作数组。
那么数组的类究竟是什么?
奇怪的”[I”
其实要想知道一个对象究竟是属于哪一个类,其实很简单。通过Object类下的getClass()方法,以及Class类的getName()方法就可以搞定:
public class TestArray2 {
public static void main(String[] args) {
int[] array = new int[5];
Class clazz = array.getClass();
System.out.println("array的类名是:" + clazz.getName());
}
}
输出:
array的类名是:[I
没错,就是 “[I”,非但不属于任何包,而且名称还不是合法的标识符。
这个”[I”代表什么含义,让我们摸不到头脑。我们再创建一个String类型的二维数组对比一下:
public class TestArray2 {
public static void main(String[] args) {
int[] array1 = new int[5];
String[][] array2 = new String[5][2];
Class clazz1 = array1.getClass();
Class clazz2 = array2.getClass();
System.out.println("array1的类名是:" + clazz1.getName());
System.out.println("array2的类名是:" + clazz2.getName());
}
}
输出:
array1的类名是:[I
array2的类名是:[[Ljava.lang.String;
如此对比,我们应该看出一点眉目了。简单的说,数组的类名由若干个’[‘和数组元素类型的内部名称组成,”[“代表了数组的维度,而后面则是代表了数组元素所在的类。
其实,”[I”自身没有声明任何成员变量、成员方法、构造函数和Annotation,可以说,”[I”就是个空类。
那么这个”[I”是在哪声明的呢?
答案是在JVM中。类加载器先看看数组类是否已经被创建了。如果没有,那就说明需要创建数组类;如果有,那就无需创建了。如果数组元素是引用类型,那么类加载器首先去加载数组元素的类。JVM根据元素类型和维度,创建相应的数组类。
数组的使用方法
在我们弄清楚数组究竟是什么之后,我们关心的数组的使用方法。
数组的使用方法无非就是四个步:声明数组引用、初始化对象、赋值、操作
- 声明数组引用:通过数组的标识符在栈空间中创建一个引用,用以指向真实对象,如:
int[] array;
Person[] persons;
。此时数组还是一个尚未初始化的局部变量,编译器还不能使用它。 - 初始化对象:在堆空间中申请一块定长的连续的空间,如:
array = new int[10];
persons = new Person[5];
- 赋值就是在已经分配的空间里面放入数据,如:
array[0] = 1;
array[1] = 2;
。未经过赋值时,所有的引用被自动初始化为null
,有如下三种方法赋值:
int a[] = new int[] {1,2,3,4,5};
int b[] = {1,2,3,4,5};
int c[] = new int[3];
for(;;)
- 操作:就是对数组元素进行操作。通过数组名+有效的下标来确认数据。
数组的复制
如果我们想把一个整型a复制,那么我们就会使用int b = a;
,此时b就是a的复制;
如果我们想把一个数组a[]赋值,使用int[] b = a;
,那么会怎么样呢?
Code:
package char16;
import java.lang.reflect.Array;
import java.util.Arrays;
public class TestArray3 {
public static void main(String[] args) {
int[] a = new int[]{1,2,3,4,5};
int[] b= a;
System.out.println("a的内存是:" + a + "、a的内容是:" + Arrays.toString(a));
System.out.println("b的内存是:" + b + "、b的内容是:" + Arrays.toString(b));
}
}
输出:
a的内存地址是:[I@1540e19d、a的内容是:[1, 2, 3, 4, 5]
b的内存地址是:[I@1540e19d、b的内容是:[1, 2, 3, 4, 5]
Analysis:
我们从数值上看出,数组a与数组b的内容是相同的,表面上看是复制成功了。但是我们发现a与b的内存地址是相等的,这就说明其实并没有进行复制,这种方法只复制了数组对象的引用,而不是对象本身的拷贝,这样被成为浅复制。这样的话,如果一个数组发生改变,那么引用同一数组的变量也要发生改变,所以这种“浅复制”是行不通的。
那我们列举出几种复制数组的方法:
1、通过for循环
创建一个新的数组,使用for循环将原数组中的每一个元素都复制给这个新数组中对应位置的元素。此方法效率最差。
2、Arrays.copyof
使用Arrays工具包下的copyOf
方法:
public static boolean[] copyOf(boolean[] original,int newLength)
复制指定的数组,截取或用 false 填充(如有必要),以使副本具有指定的长度。对于在原数组和副本中都有效的所有索引,这两个数组将包含相同的值。对于在副本中有效而在原数组无效的所有索引,副本将包含 false。当且仅当指定长度大于原数组的长度时,这些索引存在。
参数:
original - 要复制的数组
newLength - 要返回的副本的长度
返回:
原数组的副本,截取或用 false 元素填充以获得指定的长度
Arrays.copyof()源码如下:
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
@SuppressWarnings("unchecked")
//首先给用来装拷贝的数组copy设定长度
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
//底层还是用的是System.arraycopy()数组
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
3、System.arraycopy
使用System包下的arraycopy
方法,没错,System包除了有标准输入、标准输出和错误输出流;对外部定义的属性和环境变量的访问;加载文件和库的方法;还有快速复制数组的一部分的实用方法。
System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
从指定源数组中复制一个数组,复制从指定的位置开始,到目标数组的指定位置结束。
参数:
src - 源数组。
srcPos - 源数组中的起始位置。
dest - 目标数组。
destPos - 目标数据中的起始位置。
length - 要复制的数组元素的数量。
要注意的地方有两点:
1. 两个数组是相同的数据类型;
2. 不能对数组进行任何越界操作。
4、clone()
java.lang.Object类的clone()方法创建并返回此对象的一个副本。“副本”的准确含义可能依赖于对象的类。方法为protected类型,不可直接调用,需要先对要克隆的类进行下列操作:
1. 首先被克隆的类实现Cloneable接口;
2. 然后在该类中覆盖clone()方法,并且在该clone()方法中调用super.clone();
3. 这样,super.clone()便可以调用java.lang.Object类的clone()方法。
//被克隆的类要实现Cloneable接口
class Cat implements Cloneable
{
private String name;
private int age;
public Cat(String name,int age)
{
this.name=name;
this.age=age;
}
//重写clone()方法
protected Object clone()throws CloneNotSupportedException{
return super.clone() ;
}
}
public class Clone {
public static void main(String[] args) throws CloneNotSupportedException {
Cat cat1=new Cat("xiaohua",3);
System.out.println(cat1);
//调用clone方法
Cat cat2=(Cat)cat1.clone();
System.out.println(cat2);
}
}
对于以上四种方法,效率:System.arraycopy > clone > Arrays.copyOf > for循环
返回一个数组
通常我们要返回、打印一个数组的时候,使用for循环方法。但是这种方法优点麻烦,因此我们可以使用Arrays工具类提供的toString()方法来打印一个数组。
多维数组
在实际编程中,我们用到的最多的多维数组是二维数组,例如一个棋盘:int[][] checkerboard = int[64][64];
多维数组的相关知识点,其实并不难,我们可以通过一段代码来理解二维数组的实现方法:
package char16;
import java.util.Locale;
/**
* 编写一个方法,能够产生二维双精度型数组并加以初始化。数组的容量由方法的形式参数决定
* 其初值必须落在另外两个形式参数所制定的区间内。
* 编写第二个放大,打印出第一个方法所产生的数组。
*/
public class E03_TwoDDoubleArray {
public static double[][] twoDDoubleArray(int xLen, int yLen, double valStart, double valEnd) {
//声明并创建以一个二维数组,并使用双重for循环为其赋值
double[][] array = new double[xLen][yLen];
double increment = (valEnd - valStart)/(xLen * yLen);
double val = valStart;
for(int i = 0; i < array.length; i++)
for(int j = 0; j < array[i].length; j++) {
array[i][j] = val;
val += increment;
}
return array;
}
//打印二维数组
public static void printArray(double[][] array) {
for(int i = 0; i < array.length; i++) {
for(int j = 0; j < array[i].length; j++)
System.out.printf("%.2f ",array[i][j]);
System.out.println();
}
}
public static void main(String args[]) {
double[][] twoD = twoDDoubleArray(4, 6, 47.0, 99.0);
printArray(twoD);
System.out.println("**********************");
double[][] twoD2 = twoDDoubleArray(2, 2, 47.0, 99.0);
printArray(twoD2);
System.out.println("**********************");
double[][] twoD3 = twoDDoubleArray(9, 5, 47.0, 99.0);
printArray(twoD3);
}
}
数组和反射
Java反射技术除了可以在运行时动态地决定要创建什么类型的对象,访问哪些成员变量,方法,还可以动态地创建各种不同类型,不同维度的数组。
动态创建数组的步骤如下:
- 创建Class对象,通过forName(String)方法指定数组元素的类型;
- 调用Array.newInstance(Class, length_of_array)动态创建数组;
Code:
package char16;
import java.lang.reflect.Array;
public class TestArray4 {
public static void main(String args[]) throws Exception{
//动态的创建了一个Object类型的新数组,
Class clazz = Class.forName("java.lang.Integer");
Object temp = Array.newInstance(clazz,3);
Class<?> c = temp.getClass().getComponentType() ; // 取得数组的Class对象
System.out.println("类型:" + c.getName()) ; // 取得数组类型名称
System.out.println("长度:" + Array.getLength(temp)) ;
System.out.println("第一个内容:" + Array.get(temp,0)) ;
Array.set(temp,0,6) ;
System.out.println("第一个内容:" + Array.get(temp,0)) ;
}
}
输出:
类型:java.lang.Integer
长度:3
第一个内容:null
第一个内容:6
Analysis:
我们要注意的是,这是一个Object类型的temp,而不是Object[]类型的。虽然在API中之处Array.newInstance()
的返回值是一个新数组,但是我们不可以使用Arrays工具类下的public static String toString(Object[] a)
,
而是使用Array类下的public static Object get(Object array, int index)
方法来获取、打印数组(因为参数是Object)
我不太明白Object类型的对象为什么是一个数组,但是Object包中有判断这个对象是否是一个数组的方法:
package char16;
import java.lang.reflect.Array;
public class TestArray5 {
public static void main(String[] args) {
String[] obj=new String[]{"a","b","c"};
printArrays(obj);
printArrays("xyz");
}
private static void printArrays(Object obj) {
Class clazz=obj.getClass();
if(clazz.isArray()) {
int len= Array.getLength(obj);
for(int i=0;i<len;i++) {
System.out.println(Array.get(obj,i));
}
} else {
System.out.println(obj);
}
}
}
其实,数组类型与Object类型的关系如下:
- 具有相同维数和元素类型的属于属于同一个类型。及具有相同的Class实例对象。
- 代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。
- 基本类型的一维数组可以被当作Object类型使用,不能当做Object[]类型使用;
- 非基本类型的一维数组,即可以当做Object类型使用,又可以当做Object[]类型使用。
- 二维数组,可以当做Object类型使用,又可以当做Object[]类型使用。
重点:
public class ArrayReflectTest {
public static void main(String[] args) {
int[] a1=new int[]{1,2,3};
int[] a2=new int[4];
int[][] a3=new int[2][3];
String[] a4=new String[]{"a","b","c"};
System.out.println(a1.getClass()==a2.getClass());
//返回true因为符合(具有相同维数和元素类型的属于属于同一个类型。及具有相同的Class实例对象。);
System.out.println(a1.getClass()==a3.getClass());
//返回false,因为a1数组的类型是基本数据类型的数组,其类型属于Object类型,而a3是引用类型的数组,其类型属于Object[]类型。即类型不同和维数也不同
System.out.println(a1.getClass()==a4.getClass());
//返回false因为a1数组的类型是基本数据类型的数组,而a4是引用类型的数组。即类型不同
Object obj1=a1;
//Object[] obj2=a2; 编译错误
//基本类型的一维数组只能被当作Object类型使用,不能当做Object[]类型使用
Object[] obj3=a3;
Object obj4=a3;
//基本类型的二维数组,可以当做Object类型使用,又可以当做Object[]类型使用。
Object[] obj5=a4;
Object obj6= a4;
//非基本类型的一维数组,即可以当做Object类型使用,又可以当做Object[]类型使用。
}
}
数组与泛型
在Java中,不支持泛型数组,即不能实例化具有参数化类型的数组:
ArrayList<String>[] as = new ArrayList<String>[10];
其根本愿意是:数组在创建的时候必须知道内部元素的类型,而且一直都会记得这个类型信息,没此王数组里添加元素都会做类型检查。
但是因为Java数组是用擦除(Erasure)实现的,运行时类型会被擦出掉。比如:
List<String> list = new ArrayList<String>();
list.add("Hello");
String str = list.get(0);
代码运行时,类型参数被擦除掉,只有在最后读取内部元素的时候,才插入一个类型转换,这看起来好像是在读取前加入一个强转:
List<String> list = new ArrayList<String>();
list.add("Hello");
String str = (String)list.get(0);
因此,若初始化泛型数组,编译器看不到泛型的参数类型,而数组由于无法确定所持有元素的类型,因而不能初始化。
所以Java中泛型和数组可谓水火不相容。
java.lang.reflect.Array
public final class Arrayextends ObjectArray 类主要提供了三种类型的方法:
动态创建一个数组:也就是
newInstance
,这个在前面提了set系列方法:
public static void set(Object array,int index,Object value)
将指定数组对象中索引组件的值设置为指定的新值。如果数组的类型为基本组件类型,则新值第一个被自动解包。
参数:
array - 数组
index - 数组内部的索引
value - 索引组件的新值get系列方法:
public static Object get(Object array,int index)throws IllegalArgumentException,ArrayIndexOutOfBoundsException
返回指定数组对象中索引组件的值。如果该值是一个基本类型值,则自动将其包装在一个对象中。
参数:
array - 数组
index - 索引
返回:
指定数组中索引组件的(可能已封装的)值
java.util.Arrays
在java.util类库中的Arrays类是操作数组的“工具箱”,里面是一套用于数组的static实用方法。
这一套方法,我把它们大致归为以下几类,分别是:
asList
,fill
,hasCode
,copyof
,equals
,binarySearch
,sort
,toString
,deep系列
还有ArrayList内部类
,下面我们就开始逐一分析:
1. asList
public static <T> List<T> asList(T... a)
返回一个受指定数组支持的固定大小的列表。(对返回列表的更改会“直接写”到数组。)此方法同 Collection.toArray() 一起,充当了基于数组的 API 与基于 collection 的 API 之间的桥梁。返回的列表是可序列化的,并且实现了 RandomAccess。
此方法还提供了一个创建固定长度的列表的便捷方法,该列表被初始化为包含多个元素:
// 在快速创建list时比较实用,数组是基本数据类型时不要使用
List<String> stooges = Arrays.asList("Larry", "Moe", "Curly");
参数:
a - 支持列表的数组。
返回:
指定数组的列表视图。
对于该方法的相关特性总结如下:
该方法对于基本数据类型的数组支持不好,当数组是基本数据类型时使用会出错(具体原因在下面解释)。
当使用asList()方法时,数组就和列表链接在一起了.当更新其中之一时,另一个将自动获得更新。 注意:仅仅针对对象数组类型,基本数据类型数组不具备该特性。
asList得到的数组是的没有add和remove方法的。
通过查看Arrays类的源码可以知道,asList返回的List是Array中的实现的 内部类ArrayList,而该类并没有定义add和remove方法。另外,为什么修改其中一个,另一个也自动获得更新了?因为asList获得List实际引用的就是数组。
使用asList()方法将其转换成列表有时候会出现莫名其妙的问题。如下:
public static void main(String[] args) {
int[] datas = new int[]{1,2,3,4,5};
List list = Arrays.asList(datas);
System.out.println(list.size());
}
------------Output:
1
结果是1,这和我们期望的结果5不相符。为什么会这样呢?先看asList()的源码:
public static<T> List<T> asList(T... a) {
return new ArrayList<T>(a);
}
这是一个泛型方法。注意这个参数:T…a
,这个参数是一个T类型的变长参数,我们知道基本数据类型是不可能泛型化的,也是就说基本数据类型不可作为泛型参的。但是为什么编译器没有报错呢?
这是因为在java中,一个数组的整体会当做一个对象来处理,它是可以泛型的,所以我们的程序是把一个int型的数组作为了T的类型,所以在转换之后List中就只会存在一个类型为int数组的元素了。
所以我们这样的程序System.out.println(datas.equals(list.get(0)));
输出结果肯定是true。当然如果将int改为Integer,则长度就会变成5了。
我们在看下面程序:
enum Week{Sum,Mon,Tue,Web,Thu,Fri,Sat}
public static void main(String[] args) {
Week[] weeks = {Week.Sum,Week.Mon,Week.Tue,Week.Web,Week.Thu,Week.Fri};
List<Week> list = Arrays.asList(weeks);
list.add(Week.Sat);
}
这个程序非常简单,就是讲一个数组转换成list,然后add一个值,但是运行会报错:
Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.AbstractList.add(AbstractList.java:131)
at java.util.AbstractList.add(AbstractList.java:91)
at com.array.Test.main(Test.java:18)
编译没错,但是运行时出现了异常错误:UnsupportedOperationException ,当不支持请求的操作时,就会抛出该异常。这说明list不支持add方法,很显然这是不可能的,add()就是List中的一个方法。什么原因引起这个异常呢?还是回到asList()的源代码:
public static <T> List<T> asList(T... a) {
return new ArrayList<T>(a);
}
这里是直接返回一个ArrayList对象返回,点进去我们发现:这个ArrayList是Arrays工具类的一个内部类,点进去看源码:
private static class ArrayList<E> extends AbstractList<E>
implements RandomAccess, java.io.Serializable{
private static final long serialVersionUID = -2764017481108945198L;
private final E[] a;
ArrayList(E[] array) {
if (array==null)
throw new NullPointerException();
a = array;
}
/** 省略方法 **/
}
但是这个内部类并没有提供add()方法,那么查看父类:
public boolean add(E e) {
add(size(), e);
return true;
}
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
这里父类仅仅只是提供了方法,方法的具体实现却没有,所以具体的实现需要子类自己来提供,但是这个内部类ArrayList并没有实现add的实现方法。在ArrayList中,它主要提供了如下几个方法:
- size:元素数量
- toArray:转换为数组,实现了数组的浅拷贝。
- get:获得指定元素。
- contains:是否包含某元素。
重点来了!!!
所以综上所述,asList返回的是一个长度不可变的列表。数组是多长,转换成的列表是多长,我们是无法通过add、remove来增加或者减少其长度的。
如果想根据数组得到一个新的正常的list,当然可可以循环一个一个添加,也可以才有以下2个种方法:
ArrayList<Integer> copyArrays=new ArrayList<Integer>(Arrays.asList(ob));
//这样就是得到一个新的list,可对其进行add,remove了
copyArrays.add(222);
//正常,不会报错
Collections.addAll(new ArrayList<Integer>(5), ob);
//或者新建一个空的list,把要转换的数组用Collections.addAll添加进去
2. fill
该方法的作用十分有限:
public static void fill(xxx[] a,xxx val)将指定的 xxx类型的值value,分配给指定 xxx 型数组a的每个元素。
参数:
a - 要填充的数组
val - 要存储在数组所有元素中的值
3. hasCode
public static int hashCode(int[] a)
基于指定数组的内容返回哈希码。对于任何两个满足 Arrays.equals(a, b) 的非 null 的 int 型数组 a 和 b,也可以说 Arrays.hashCode(a) == Arrays.hashCode(b)。
此方法返回的值与在 List 上调用 hashCode 方法获得的值相同,该 List 包含以相同顺序表示 a 数组元素的 Integer 实例的序列。如果 a 为 null,则此方法返回 0。
参数:
a - 要计算其哈希值的数组
返回:
a 数组基于内容的哈希码
4. copyof
关于数组的几种复制方法在前面讲过
public static int[] copyOf(int[] original,int newLength)
复制指定的数组,截取或用 0 填充(如有必要),以使副本具有指定的长度。对于在原数组和副本中都有效的所有索引,这两个数组将包含相同的值。对于在副本中有效而在原数组无效的所有索引,副本将包含 0。当且仅当指定长度大于原数组的长度时,这些索引存在。
参数:
original - 要复制的数组
newLength - 要返回的副本的长度
返回:
原数组的副本,截取或用 0 填充以获得指定的长度
5. equals
Arrays类提供了重载后的equals()方法,用来比较整个数组。数组相等的条件是元素个数必须相等,并且对应位置的元素也相等。要注意的是,不是我们之前习惯的a1.equals(a2);
,而是Arrays.equals(a1,a2);
API如下:
public static boolean equals(Object[] a, Object[] a2)
如果两个指定的 Objects 数组彼此相等,则返回 true。如果两个数组包含相同数量的元素,并且两个数组中的所有相应元素对都是相等的,则认为这两个数组是相等的。如果 (e1==null ? e2==null : e1.equals(e2)),则认为 e1 和 e2 这两个对象是相等的 。换句话说,如果两个数组以相同顺序包含相同的元素,则两个数组是相等的。此外,如果两个数组引用都为 null,则认为它们是相等的。
参数:
a - 将测试其相等性的一个数组
a2 - 将测试其相等性的另一个数组
返回:
如果两个数组相等,则返回 true
我们看下面的代码:
package char16;
import java.lang.reflect.Array;
import java.util.Arrays;
public class CompareArrays {
public static void main(String[] args) {
int[] a1 = new int[10];
int[] a2 = new int[10];
Arrays.fill(a1,66);
Arrays.fill(a2,66);
System.out.println(Arrays.equals(a1,a2));
a2[3] = 11;
System.out.println(Arrays.equals(a1,a2));
String[] s1 = new String[4];
Arrays.fill(s1,"Hi");
String[] s2 = {new String("Hi"),new String("Hi"),new String("Hi"),new String("Hi")};
System.out.println(Arrays.equals(s1,s2));
}
}
输出:
true
false
true
s1的所有元素都指向同一个对象,而s2包含五个相互独立的对象。然而数组相等是基于内容的(),所以结果为true。
Arrays.equals()源码如下:
public static boolean equals(Object[] a, Object[] 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++) {
Object o1 = a[i];
Object o2 = a2[i];
if (!(o1==null ? o2==null : o1.equals(o2)))
return false;
}
return true;
}
6. toString
返回指定数组的内容的字符串表示形式。
字符串表示由数组元素的列表组成,括在方括号( “[]” )中。 相邻的元素由字符”, “分隔(逗号后跟一个空格)。 元素将转换为字符串,如String.valueOf(long) 。 如果a是null返回”null”。
//基本数据类型转字符串
public static String toString(long[] 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(", ");
}
}
//Object类型使用valueOf方法转字符串
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(", ");
}
}
7. sort
public static void sort(int[] a)
:对指定的 int 型数组按数字升序进行排序。
我们看一下sort()的源码:
public static void sort(int[] a) {
DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
}
使用的是一个经过调优的快速排序法。
8. binarySearch
public static int binarySearch(int[] a,int key)
使用二分搜索法来搜索指定的 int 型数组,以获得指定的值。必须在进行此调用之前对数组进行排序(通过 sort(int[]) 方法)。如果没有对数组进行排序,则结果是不确定的。如果数组包含多个带有指定值的元素,则无法保证找到的是哪一个。
参数:
a - 要搜索的数组
key - 要搜索的值
返回:
如果它包含在数组中,则返回搜索键的索引;否则返回 (-(插入点) - 1)。插入点 被定义为将键插入数组的那一点:即第一个大于此键的元素索引,如果数组中的所有元素都小于指定的键,则为 a.length。注意,这保证了当且仅当此键被找到时,返回的值将 >= 0。
源码:
public static int binarySearch(int[] a, int key) {
return binarySearch0(a, 0, a.length, key);
}
deepEquals
如果两个指定的数组彼此深度相等 ,则返回true 。
那么我们就要知道,什么是深度相等?是值多位数组的深度。
下面看代码:
import java.util.Arrays;
public class Test {
public static void main(String[] args) {
String[][] name1 = {{ "G","a","o" },{ "H","u","a","n"},{ "j","i","e"}};
String[][] name2 = {{ "G","a","o" },{ "H","u","a","n"},{ "j","i","e"}};
System.out.println(Arrays.equals(name1, name2)); // false
System.out.println(Arrays.deepEquals(name1, name2));// true
}
}
import java.util.Arrays;
public class Test {
public static void main(String[] args) {
String[] name1 = {"G","a","o","H","u","a","n","j","i","e"};
String[] name2 = {"G","a","o","H","u","a","n","j","i","e"};
System.out.println(Arrays.equals(name1, name2)); // true
System.out.println(Arrays.deepEquals(name1, name2)); // true
}
}
总结:
- deepEquals用于判定两个指定数组彼此是否深层相等,此方法适用于任意深度的嵌套数组。
- equals用于判定两个数组是否相等,如果两个数组以相同顺序包含相同元素,则返回true,否则返回false。
- 通过比对“代码一”和“代码二”我们可以得出这样一个结论:如果两个数组使用equals返回true,则使用deepEquals也返回true,也就是说在比较的两个数组均为一维数组的前提下,equals和deepEquals的比较结果没有差别;
- 如果要比较多为数组,则需要使用deepEquals方法;
下面是源码:
//深度比较两个数组是否相等
public static boolean deepEquals(Object[] a1, Object[] a2) {
if (a1 == a2)
return true;
if (a1 == null || a2==null)
return false;
int length = a1.length;
if (a2.length != length)
return false;
for (int i = 0; i < length; i++) {
Object e1 = a1[i];
Object e2 = a2[i];
if (e1 == e2)
continue;
if (e1 == null)
return false;
// Figure out whether the two elements are equal
boolean eq;
if (e1 instanceof Object[] && e2 instanceof Object[])
eq = deepEquals ((Object[]) e1, (Object[]) e2);
else if (e1 instanceof byte[] && e2 instanceof byte[])
eq = equals((byte[]) e1, (byte[]) e2);
else if (e1 instanceof short[] && e2 instanceof short[])
eq = equals((short[]) e1, (short[]) e2);
else if (e1 instanceof int[] && e2 instanceof int[])
eq = equals((int[]) e1, (int[]) e2);
else if (e1 instanceof long[] && e2 instanceof long[])
eq = equals((long[]) e1, (long[]) e2);
else if (e1 instanceof char[] && e2 instanceof char[])
eq = equals((char[]) e1, (char[]) e2);
else if (e1 instanceof float[] && e2 instanceof float[])
eq = equals((float[]) e1, (float[]) e2);
else if (e1 instanceof double[] && e2 instanceof double[])
eq = equals((double[]) e1, (double[]) e2);
else if (e1 instanceof boolean[] && e2 instanceof boolean[])
eq = equals((boolean[]) e1, (boolean[]) e2);
else
eq = e1.equals(e2);
if (!eq)
return false;
}
return true;
}
deepToString
public static String deepToString(Object[] a)
返回指定数组的“深度内容”的字符串表示形式。 如果数组包含其他数组作为元素,则字符串表示包含其内容等等。 此方法用于将多维数组转换为字符串。
字符串表示由数组元素的列表组成,括在方括号( “[]” )中。 相邻的元素由字符”, ” (逗号后跟一个空格)分隔开。 元件由String.valueOf(Object)转换为字符串,除非它们本身阵列。
如果一个元素e是一个基本类型的阵列,它通过调用的Arrays.toString(e)适当重载转换为字符串作为。 如果元素e是引用类型的数组,则通过递归调用此方法将其转换为字符串。
为了避免无限递归,如果指定的数组包含自身作为元素,或者通过一个或多个数组级别包含对其自身的间接引用,则将自引用转换为字符串”[…]” 。 例如,仅包含对其自身的引用的数组将被呈现为”[[…]]” 。
//深度转换字符串
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());
return buf.toString();
}
private static void deepToString(Object[] a, StringBuilder buf,
Set<Object[]> dejaVu) {
if (a == null) {
buf.append("null");
return;
}
dejaVu.add(a);
buf.append('[');
for (int i = 0; i < a.length; i++) {
if (i != 0)
buf.append(", ");
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());
}
}
}
buf.append(']');
dejaVu.remove(a);
}
ps:用心学习,喜欢的话请点赞 (在左侧哦)