Java刷题————Arrays

一. Arrays

在java.util包下,也符合它的定位——关于数组的工具类:

1)Arrays.fill()

fill() 方法应该是最常用的方法,特别是int类型数组的整体赋值,e.g.

但其实它有多个重载版本,包括给对象数组赋值:

为了方便,我们直接看“父版本”的实现,很简单:

相当于Arrays帮你写好的一个for循环而已,填进去数组名和想赋的值即可,e.g.:

但这里有几个注意点:

① 除了整体赋值,每个数据类型的fill()都有对应的指定范围的赋值方法,还是看“父方法”的实现:

注意,像String.substring(start, end)一样,fill(arr, start, end)只会将arr[start]到arr[end-1]赋上值。

② 虽然上面都是看“父方法”——Object[]的实现,但是在真要对对象数组整体赋值时要注意,Object[] a中每个数组元素都是一个对象引用,当如下使用时:

通过源码我们知道, stuArr的每个元素都是指向heap上对象{“Sam”,10}的引用(student也是),如果之后的操作中只是修改引用本身,那是没关系的,如重新赋值,交换等操作,但是不能操作对象内的属性如本例中的name和age,否则将引起所有指向该对象的引用的改变(这一点其实很好理解,但是在使用了Arrays.fill()方法后容易想当然忽略了)。当然,每个元素都赋过各自独立的值(指向各自的对象)之后,就没关系了。

③ 对于数组,数组引用在栈上,数组元素和数组元素指向的对象(如果数据类型不是基本类型的话)都是在堆上,故像int[] arr = new int[100],默认就是100个0,就用不着再Arrays.fill(arr,0)了,其他几种类型同理。

 

2)Arrays.sort()

sort方法应该是使用第二多的方法,看一下使用最多的以int[]为参数的实现吧:

很明显,调用了DualPivotQuicksort()。所以很明显了,当我们只是“无知”地如下调用Arrays.sort()方法时:

其实Arrays使用的是Java的DualPivotQuicksort()方法,而同上面的fill()方法一样,sort()方法也有指定范围的排序:

同样要注意,toIndex是数组长度(好像Java对外暴露的常用API都是这个规律,但如DualPivotQuicksort.sort()这种主要供内部使用的,则用真正的下标)。

除了上面之外,Arrays.sort()也有几个注意点:

① 虽然上面我们看到了Arrays.sort()直接调用DualPivotQuicksort.sort()方法, 顾名思义,即双轴快排,那么真的不论数组大小,是否相对有序等等条件,Java都默认使用双轴快排吗?这是不是不符合常理呢,可不能望文生义啊,还是要看源码:

怎么这么多常数?(除了图片中的还有一堆),随便翻看一个的注释:

意思是数组长度小于这个值的时候,insertion sort是优先于quicksort的。

再随便看一下参数类型为int[]的sort方法实现:

看到没,当小于这个常数时,使用的一个策略,然后直接return了;若大于等于这个常数,则又使用一个策略。其实阅读源码和查找资料就知道,对于不同的数组类型和长度,DualPivotQuicksort.sort()会采取不同的策略,比如小于长度小于47时,采用插入排序而不是双轴快排。

所以,千万不能被类名迷惑了——DualPivotQuicksort,具体还是要看代码实现。

关于DualPivotQuicksort.sort()的实现策略,会在其他专题里专门总结,这里不再赘述。

② Arrays.sort()除了boolean之外7大类型的排序:

但是单单就这张图而言,其实有很多问题,如:

a)如图标注,除了7大类型外,Arrays.sort()还提供了Object[]和 泛型T[]的排序(最后两个是泛型T[]),那么,对于一个空白的,连比较标准都没有指定的Object,Arrays.sort()怎么排序呢:

结果很明显,提示说Object类型无法转换为(be cast to)Comparable类型,由于Comparable是接口,即这是提示Object没有实现Comparable接口。如果Object太“光溜溜”了,会不会“正常的”POJO类就可以了呢,看看之前写过的Student例子:

一样的,提示没有实现Comparable接口,这也符合常理:都没有指定什么是大,什么是小的标准,让sort怎么排呢(必然按照大小或小大的顺序),而这个指定相对大小的标准,在Comparable中表现的就是实现其唯一方法——compareTo():

现在让Student实现Comparable接口:

再看排序结果:

Ok了,这里提一下,实现Comparable接口有两个注意点,对比Comparable接口的声明和上例Student的实现可以看得更清楚:一是要指定泛型类型,即紧随Comparable接口后的那个T,这也是compareTo()形参的类型;二是compareTo()方法的返回类型为int,约定俗称是返回-1代表小于,返回0代表等于,返回1代表大于。

b)

再看这张表,a)中解决两个形参类型为Object[]的问题,还有两个泛型T[],可以看到还有一个Comparator参数,我们知道,Comparator被称为“补救措施”的比较器。这其实可以从单词意义看出来:Comparable——可比较的,形容词,Comparator——比较器,名词。即实现了Comparable即具有了比较的“能力”,而Comparator只是一个比较器,相当于“器具”,并不具有赋能的作用。

废话不多说,看一下没有实现了Comparable接口,还想Arrays.sort()方法该怎么做:

Student类:

使用Arrays.sort();

功能上实现了,再回看Comparator接口:

但其实还有很多其他的方法:

但最核心的就是int compare(T o1, T o2)方法,同Comparable一样,首先要注意Comparator后面要指定泛型类型,以便在compare和其他方法中使用,然后注意int compare(T o1, T o2)返回的是int类型,同Comparable一样,负数表示小于,0表示大于,正数表示小于。但是可以看到,Comparator的compare的方法有两个参数,其实“负数表示小于,0表示大于,正数表示小于”完整表达是“负数表示第一个数小于第二个数,0表示第一个数大于第二个数,正数表示第一个数小于第二个数”,从这也能看出Comparator和Comparable的区别:Comparable是在类内部使用的,故compareTo(T t)只需指定要比较的对象;而Comparator在类外部使用,故compare(T o1, T o2)方法需要指定进行比较的两个对象。

c)

再看这张表,有个问题:对于7大类型,都是基本类型作为参数,那么当包装类型作为参数传入会发生写什么呢?先分析一下:

一. 自动拆箱,但是Integer[]是并不能拆箱为int[]的,换言之,传入Integer[]却想使用sort(int[])是行不通的;二..传入Integer[]想使用sort(Object[]),先来看看结果吧(并不知道使用了那个方法):

竟然成功运行了,看下字节码:

可以看到,“([Ljava/lang/Object;)V”——Arrays.sort(intArr)调用的是void sort(Object[])方法。

那么,为什么Student不可以直接使用Arrays.sort()方法,而Integer却可以呢?秘密就在于Integer源码:

都已经实现了Comparator方法,Ok?

实际上,除了Integer,所有的包装类都实现了Comparable接口,对,包括Boolean,即使它的实现有点怪。。:

另外,String类同样实现了Comparable接口:

可以看到,String类的“顺序”就是字典序。

 

3)Arrays.binarySearch()

这个API很nice,特别在做算法题的时候,法如其名,二分法,而且多种重载版本,并提供特定区间的二分搜索:

看下 binarySearch(int[] a, int fromIndex, int toIndex, int key)

并且可以看到,由于Comparable和Comparator可以“定义”对象大小,故Arrays.binarySearch()同样可以支持对象的二分搜索。

 

Arrays.sort还有其他的一些API,例如给基本类型数组转为字符串的toString(), 给基本类型数组算哈希码的hashCode()等等,但是都不常用,这里就不介绍了。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值