Java面试--Arrays、Collections、Objects

Arrays、Collections、Objects 常用方法源码解析

我们在工作中都会写工具类,但如何才能使写出来的工具类更好用,也是有一些技巧的。本章内容以三种平时工作中经常使用的工具类为例,从使用案例出发,再看看底层源码的实现,看看能否学习到一些工具类的技巧,以及三种工具类的实际使用场景。
1.工具类通用的特征
再看细节之前,我们先总结一下好的工具类都有哪些通用的特征写法:
1.构造器必须是私有的。这样的话,工具类就无法被 new 出来,因为工具类在使用的时候,无需初始化,直接使用即可,所以不会开放出构造器出来。
2.工具类的工具方法必须被 static、final 关键字修饰。这样的话就可以保证方法不可变,并且可以直接使用,非常方便。我们需要注意的是,尽量不在工具方法中,对共享变量有做修改的操作访问(如果必须要做的话,必须加锁),因为会有线程安全的问题。除此之外,工具类方法本身是没有线程安全问题的,可以放心使用。
2.Arrays
Arrays 主要对数组提供了一些高效的操作,比如说排序、查找、填充、拷贝、相等判断等等。我们选择其中两三看下
2.1.排序
Arrays.sort 方法主要用于排序,入参支持 int、long、double 等各种基本类型的数组,也支持自定义类的数组,下面我们写个 demo 来演示一下自定义类数组的排序:
实体类:
在这里插入图片描述
测试类:
在这里插入图片描述
从输出的结果中可以看到,排序之后的数组已经是有顺序的了,也可以看到 sort方法支持两个入参:要排序的数组和外部排序器。大家都说 sort 方法排序的性能较高,主要原因是 sort 使用了双轴快速排序算法,具体算法就不细说了。
2.2.二分查找法
Arrays.binarySearch 方法主要用于快速从数组中查找出对应的值。其支持的入参类型非常多,如 byte、int、long 各种类型的数组。返回参数是查找到的对应数组下标的值,如果查询不到,则返回负数

在这里插入图片描述
我们写了一个 demo 如下:
在这里插入图片描述
从上述代码中我们需要注意两点:
1.如果被搜索的数组是无序的,一定要先排序,否则二分搜索很有可能搜索
不到,我们 demo 里面也先对数组进行了排序;
2.搜索方法返回的是数组的下标值。如果搜索不到,返回的下标值就会是负数,这时我们需要判断一下正负。如果是负数,还从数组中获取数据的话,会报数组越界的错误。demo 中对这种情况进行了判断,如果是负数,会提前抛出
明确的异常。接下来,我们来看下二分法底层代码的实现:
在这里插入图片描述
二分的主要意思是每次查找之前,都找到中间值,然后拿我们要比较的值和中间值比较,根据结果修改比较的上限或者下限,通过循环最终找到相等的位置索引,以上代码实现比较简洁,大家可以在自己理解的基础上,自己复写一遍。
2.3.拷贝
数组拷贝我们经常遇到,有时需要拷贝整个数组,有时需要拷贝部分,比如ArrayList 在 add(扩容) 或 remove(删除元素不是最后一个) 操作时,会进行一些拷贝。拷贝整个数组我们可以使用 copyOf 方法,拷贝部分我们可以
使用 copyOfRange 方法,以 copyOfRange 为例,看下底层源码的实现:
在这里插入图片描述
从 源 码 中 , 我 们 发 现 , Arrays 的 拷 贝 方 法 , 实 际 上 底 层 调 用 的 是System.arraycopy 这个 native 方法,如果你自己对底层拷贝方法比较熟悉的话,也可以直接使用。
在这里插入图片描述
3.Collections
Collections 是为了方便使用集合而产生的工具类,Arrays 方便数组使用,Collections 是方便集合使用。在实际的应用中Collections 用的还是很多的;Collections 也提供了 sort 和 binarySearch 方法,sort 底层使用的就是Arrays.sort 方法,binarySearch 底层是自己重写了二分查找算法,实现的逻辑和 Arrays 的二分查找算法完全一致,这两个方法上 Collections 和 Arrays的内部实现很类似,接下来我们来看下 Collections 独有的特性。
3.1 求集合中最大、小值
提供了 max 方法来取得集合中的最大值,min 方法来取得集合中的最小值,max 和 min 方法很相似的,我们以 max 方法为例来说明一下,max 提供了两种类型的方法,一个需要传外部排序器,一个不需要传排序器,但需要集合中的元素强制实现Comparable 接口,后者的泛型定义很有意思,我们来看下(从右往左看)
在这里插入图片描述
从这段源码中,我们可以学习到两点:
1.max 方法泛型 T 定义得非常巧妙,意思是泛型必须继承 Object 并且实现Comparable 的接口。一般让我们来定义的话,我们可以会在方法里面去判断有无实现 Comparable 的接口,这种是在运行时才能知道结果。而这里泛型直接定义了必须实现 Comparable 接口,在编译的时候就可告诉使用者,当前类没有实现 Comparable 接口,使用起来很友好;
2.给我们提供了实现两种排序机制的好示例:自定义类实现 Comparable 接口和传入外部排序器。两种排序实现原理类似,但实现有所差别,我们在工作中如果需要些排序的工具类时,可以效仿。

例如:Interger 就实现了 Comparable 接口

在这里插入图片描述
1.对于 Collection 的数据,可以使用 Collections 工具获取最大值和最小值。最大值和最小值的下标可以通过 List 的 indexOf 方法获取
在这里插入图片描述
2.对于 Map 的数据,可以先将 Map 的 value 转成 Collection,然后通过 Collections工具获取最大值和最小值。至于最大值和最小值的下标只能通过遍历来获取。
在这里插入图片描述
3.2.数组转集合 Collections.addAll
在这里插入图片描述
3.3.集合反转 Collections.reverse

在这里插入图片描述
3.4.判断的空集合和空 Map

在这里插入图片描述
3.5.多种类型的集合
Collections 对原始集合类进行了封装,提供了更好的集合类给我们,一种是线程安全的集合,一种是不可变的集合,针对 List、Map、Set 都有提供,我们先来看下线程安全的集合:
3.5.1 线程安全的集合
线程安全的集合方法都是 synchronized 打头的,如下:
在这里插入图片描述
从方法命名我们都可以看出来,底层是通过 synchronized 轻量锁来实现的,我们以 synchronizedList 为例来说明下底层的实现:
在这里插入图片描述
可以看到 List 的所有操作方法都被加上了 synchronized 锁,所以多线程对集合同时进行操作,是线程安全的。
3.5.1 不可变的集合
得到不可变集合的方法都是以 unmodifiable 开头的。这类方法的意思是,我们会从原集合中,得到一个不可变的新集合,新集合只能访问,无法修改;一旦修改,就会抛出异常。这主要是因为只开放了查询方法,其余任何修改操作都会抛出异常,我们以 unmodifiableList 为例来看下底层实现机制:
在这里插入图片描述
4 Objects
对于 Objects,我们经常使用的就是两个场景,相等判断和判空。
4.1 相等判断
Objects 有提供 equals 和 deepEquals 两个方法来进行相等判断,前者是判断基本类型和自定义类的,后者是用来判断数组的,我们来看下底层的源码实现:
在这里插入图片描述
从源码中,可以看出 Objects 对基本类型和复杂类型的对象,都有着比较细粒度的判断,可以放心使用。
Demo:
在这里插入图片描述
在这里插入图片描述
4.2 为空判断
在这里插入图片描述
Objects 提供了各种关于空的一些判断,isNull 和 nonNull 对于对象是否为空返回 Boolean 值,requireNonNull 方法更加严格,如果一旦为空,会直接抛出异常,我们需要根据生活的场景选择使用。
Demo:
在这里插入图片描述

  • 19
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值