深入了解java.util.Arrays的使用技巧
作者:指北君 发布时间:2023-01-10 18:01:32
创建
我们来看看,使用Arrays 怎么创建一个新的数组,一般来说,我们可以使用Arrays 的 copyOf , copyOfRange 和 fill 方法。
copyOf 和 copyOfRange
要使用copyOfRange,我们需要一个原始数组和我们想要复制的开始索引(包括)和结束索引(不包括)。 我们先定一个数组 intro。
String[] intro = new String[] { "once", "upon", "a", "time" };
String[] abridgement = Arrays.copyOfRange(storyIntro, 0, 3);
assertArrayEquals(new String[] { "once", "upon", "a" }, abridgement);
assertFalse(Arrays.equals(intro, abridgement));
要使用 copyOf ,,我们需要使用intro和一个目标数组大小,然后我们会得到一个该长度的新数组。
String[] revised = Arrays.copyOf(intro, 3);
String[] expanded = Arrays.copyOf(intro, 5);
assertArrayEquals(Arrays.copyOfRange(intro, 0, 3), revised);
assertNull(expanded[4]);
注意,如果我们的目标尺寸大于原始尺寸,copyOf会用 null 填充数组。
fill
另一种方法,我们可以创建一个固定长度的数组,就是填充,当我们想要一个所有元素都相同的数组时,这个方法很有用。
String[] stutter = new String[3];
Arrays.fill(stutter, "once");
assertTrue(Stream.of(stutter).allMatch(el -> "once".equals(el));
注意,我们需要事先将数组实例化,而不是像String[] filled = Arrays.fill("once", 3);
,因为这个特性是在语言中出现泛型之前引入的。
比较
我们先走来看看 Arrays 的比较方法
equals 和 deepEquals
我们可以使用 equals 进行简单的数组大小和内容比较。 如果我们添加一个null作为其中一个元素,内容检查就会失败。
assertTrue(Arrays.equals(new String[] { "once", "upon", "a", "time" }, intro));
assertFalse(Arrays.equals(new String[] { "once", "upon", "a", null }, intro));
当我们有嵌套或多
维数组时,我们可以使用deepEquals不仅检查顶层元素,还可以递归地执行检查。
Object[] story = new Object[] { intro, new String[] { "chapter one", "chapter two" }, end };
Object[] copy = new Object[] { intro, new String[] { "chapter one", "chapter two" }, end };
assertTrue(Arrays.deepEquals(story, copy));
assertFalse(Arrays.equals(story, copy));
注意,这里 deepEquals 是通过的,但equals却失败了。这是因为deepEquals在每次遇到数组时都会调用自己,而equals只是比较子数组的引用。
hashCode 和 deepHashCode
我们使用hashCode来计算一个基于数组内容的整数
Object[] looping = new Object[]{ intro, intro };
int hashBefore = Arrays.hashCode(looping);
int deepHashBefore = Arrays.deepHashCode(looping);
现在,我们将原数组的一个元素设置为空,并重新计算哈希值。
intro[3] = null;
int hashAfter = Arrays.hashCode(looping);
deepHashCode检查嵌套数组的元素数量和内容是否匹配。 如果我们用deepHashCode重新计算。
int deepHashAfter = Arrays.deepHashCode(looping);
现在,我们能够看到这两个方法的不同。
assertEquals(hashAfter, hashBefore);
assertNotEquals(deepHashAfter, deepHashBefore);
deepHashCode是我们在数组上使用HashMap和HashSet等数据结构时使用的基础计算。
排序和搜索
排序
如果我们的元素是原始类型,或者它们实现了 Comparable 接口,我们可以使用sort来执行排序。
String[] sorted = Arrays.copyOf(intro, 4);
Arrays.sort(sorted);
assertArrayEquals(new String[]{ "a", "once", "time", "upon" }, sorted);
请注意,排序会使原始引用发生变化,这就是为什么我们在这里进行复制。
排序将对不同的数组元素类型使用不同的算法。原始类型使用quicksort,对象类型使用Timsort。对于一个随机排序的数组,两者的平均情况都是O(n log(n))。
从Java 8开始,parallelSort可用于并行排序, 它提供了一种使用几个Arrays.sort任务的并发排序方法。
搜索
如果我们有一个排序的数组,那么我们可以在 O(log n) 中完成,我们可以用 binarySearch 来完成这样的任务。
int exact = Arrays.binarySearch(sorted, "time");
int caseInsensitive = Arrays.binarySearch(sorted, "TiMe", String::compareToIgnoreCase);
assertEquals("time", sorted[exact]);
assertEquals(2, exact);
assertEquals(exact, caseInsensitive);
如果我们没有提供一个比较器作为第三个参数,那么 binarySearch 就默认我们的元素类型是可比较的。如果我们的数组没有被首先排序,那么 binarySearch 将不会像我们所期望的那样工作。
流
我们都知道Arrays在Java 8中进行了更新,包含了Stream API的方法,如parallelSort、stream和setAll等。
stream 使我们能够完全访问我们的数组的Stream API。
Assert.assertEquals(Arrays.stream(intro).count(), 4);
exception.expect(ArrayIndexOutOfBoundsException.class);
Arrays.stream(intro, 2, 1).count();
我们可以为流提供包容性和排他性指数,但是如果指数失序、为负数或超出范围,我们应该判断 ArrayIndexOutOfBoundsException。
转化
toString、asList和setAll给了我们几种不同的方法来转换数组。
toString和deepToString
我们可以通过toString获得原始数组的可读版本的一个好方法。
assertEquals("[once, upon, a, time]", Arrays.toString(storyIntro));
当数组有嵌套的时候,我们必须再次使用deepToString 来打印嵌套数组的内容。
assertEquals(
"[[once, upon, a, time], [chapter one, chapter two], [the, end]]",
Arrays.deepToString(story));
asList
在所有的数组方法中,最方便我们使用的是asList。我们有一个简单的方法把数组变成一个列表。
List<String> rets = Arrays.asList(storyIntro);
assertTrue(rets.contains("upon"));
assertTrue(rets.contains("time"));
assertEquals(rets.size(), 4);
返回的列表将是一个固定的长度,而且无法添加或删除元素,还要注意的是,asList会返回这个ArrayList的类型,和我们平常在使用的ArrayList 并不一样。在调试的时候,就可能是非常具有欺骗性的,我们在写的过程 * 别要注意。
setAll
通过setAll,我们可以用一个 functional interface 来设置一个数组的所有元素。下面的代码现将位置索引作为一个参数传入到getWord方法中。
String[] longAgo = new String[4];
Arrays.setAll(longAgo, i -> this.getWord(i));
assertArrayEquals(longAgo, new String[]{"a","long","time","ago"});
当然,异常处理是使用lambda的一个比较棘手的部分。所以请记住,如果lambda抛出一个异常,那么Java就不会定义数组的最终状态。
来源:https://mp.weixin.qq.com/s/pHBMvo5BwVlGQU7cTCNvuw


猜你喜欢
- 在做android图片加载的时候,由于手机屏幕受限,很多大图加载过来的时候,我们要求等比例缩放,比如按照固定的宽度,等比例缩放高度,使得图片
- 前言目前正在做的项目,为了增加用户的体验度,准备增加一些动画效果,其中底部栏中间按钮的点击事件参考了闲鱼的动效,便在此基础上仿写了该动效,并
- 第一个为大家介绍图片如何转高斯模拟:1.方法的实现:public static void updateBgToBlur(Activity a
- 本文实例为大家分享了android计算器实现加减乘除的具体代码,供大家参考,具体内容如 * :以下计算器只注重实现功能,不考虑其他BUG,只有
- 本文实例展示了C#中this指针的用法,对于初学者进一步牢固掌握C#有很大帮助,具体内容如下:一、this指针是什么:这里有一些面向对象编程
- 上篇博客我们了解了请求参数的获取,那么获取到请求参数之后,需要对参数进行出来,然后进行数据响应。那么这篇博客我们就来了解 Controlle
- springboot对kafka的client很好的实现了集成,使用非常方便,本文也实现了一个在springboot中实现操作kafka的d
- Logback TurboFilter实现日志级别等内容的动态修改可能看到这个标题,读者会问:要修改日志的级别,不是直接修改log.xxx就
- 具体代码如下所示:public class Parent { public static int a = parentStati
- Android动态修改ToolBar的Menu菜单效果图实现实现很简单,就是一个具有3个Action的Menu,在我们滑动到不同状态的时候,
- Java平台的垃圾收集机制显著提高了开发者的效率,但是一个实现糟糕的垃圾收集器可能过多地消耗应用程序的资源。在Java虚拟机性能优化系列的第
- Autowired有两种注入方式by typeby name默认使用的是byType的方式向Bean里面注入相应的Bean。例如:@Auto
- java 解决异常 2 字节的 UTF-8 序列的字节 2 无效的问题  
- 简介SpringBoot提供了HATEOAS的便捷使用方式,本文详细讲解SpringBoot提供的这些基本方法。链接LinksHATEOAS
- 逆时针画圆弧,原理:将360度分割成36份,分别标出每10度角度时的坐标点,然后将每个点连接起来。 #include <io
- 在这篇文章中,我精选了几个比较适合 Java 编码的 IDEA 主题供小伙伴们选择。另外,我自己用的是 One Dark theme 这款。
- 本文实例讲述了Android ListView的简单应用。分享给大家供大家参考,具体如下:我们今天要讲的内容是Android中ListVie
- 内存对齐的基本原则:结构(struct/class)的内置类型数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的起始位置
- Android实现环形进度条的效果图如下:自定义控件:AttendanceProgressBar代码如下:public class Atte
- java就业前需要掌握的专业技能1.熟练的使用Java语言进行面向对象程序设计,有良好的编程习惯,熟悉常用的Java API,包括集合框架、