网站首页 > java教程 正文
1 前言
数组是相同类型的、用一个标识符名称封装到一起的一个对象序列或基本类型数据序列。简单来看,数组需要你去创建和初始化,你可以通过整型下标对数组元素进行访问,数组的大小不会改变。大多数时候你只需要知道这些,但有时候你必须在数组上进行更复杂的操作,你也可能需要在数组和更加灵活的 集合 (Collection)之间做出评估。因此本章我们将对数组进行更加深入的分析。
2 数组初始化
在声明数组变量时,需要指出数组类型(数据元素类型紧跟[])和数组变量的名字。要定义一个数组引用,只需要在类型名加上方括号:
int[] a;
方括号也可放在标识符的后面,两者的含义是一样的:
int a[] ;
这种格式符合 C 和 C++ 程序员的习惯。不过前一种格式或许更合理,毕竟它表明类型是"一个 int 型数组"。
对于数组,初始化动作可以出现在代码的任何地方,但是也可以使用一种特殊的初始化表达式,它必须在创建数组的地方出现。这种特殊的初始化是由一对花括号括起来的值组成。这种情况下,存储空间的分配(相当于使用 new) 将由编译器负责。例如:
int[] a = {1, 2, 3, 4, 5};
那么为什么在还没有数组的时候定义一个数组引用呢?在 Java 中可以将一个数组赋值给另一个数组,其实真正做的只是复制了一个引用,就像下面演示的这样:
public static void main(String[] args) {
int[] a1 = {1, 2, 3, 4, 5};
int[] a2;
a2 = a1;
for (int i = 0; i < a2.length; i++) {
a2[i] += 1;
}
for (int i = 0; i < a1.length; i++) {
System.out.println("a1[" + i + "] = " + a1[i]);
}
}
输出:
a1[0] = 2;
a1[1] = 3;
a1[2] = 4;
a1[3] = 5;
a1[4] = 6;
a1 初始化了,但是 a2 没有;这里,a2 在后面被赋给另一个数组。由于 a1 和 a2 是相同数组的别名,因此通过 a2 所做的修改在 a1 中也能看到。
所有的数组(无论是对象数组还是基本类型数组)都有一个固定成员 length,告诉你这个数组有多少个元素,你不能对其修改。与 C 和 C++ 类似,Java 数组计数也是从 0 开始的,所能使用的最大下标数是 length - 1。超过这个边界,C 和 C++ 会默认接受,允许你访问所有内存,许多声名狼藉的 bug 都是由此而生。但是 Java 在你访问超出这个边界时,会报运行时错误(异常),从而避免此类问题。
2.1 动态数组
如果在编写程序时,不确定数组中需要多少个元素,可以使用 new 在数组中创建元素。new 不能创建非数组以外的基本类型数据。如果你创建了一个非基本类型的数组,那么你创建的是一个引用数组。以整型的包装类型 Integer 为例,即使使用 new 创建数组之后,直到通过创建新的 Integer 对象(通过自动装箱),并把对象赋值给引用,初始化才算结束。如果忘记了创建对象,但试图使用数组中的空引用,就会在运行时产生异常。
public static void main(String[] args) {
Integer[] a1 = {
1, 2,
3, // Autoboxing
};
Integer[] a2 = new Integer[] {
1, 2,
3, // Autoboxing
};
System.out.println(Arrays.toString(a1));
System.out.println(Arrays.toString(a2));
}
输出:
[1, 2, 3]
[1, 2, 3]
在这两种形式中,初始化列表的最后一个逗号是可选的(这一特性使维护长列表变得更容易)。
3 数组特性
明明还有很多其他的办法来保存对象,那么是什么令数组如此特别?
将数组和其他类型的集合区分开来的原因有三:效率,类型,保存基本数据类型的能力。在 Java 中,使用数组存储和随机访问对象引用序列是非常高效的。数组是简单的线性序列,这使得对元素的访问变得非常快。然而这种高速也是有代价的,代价就是数组对象的大小是固定的,且在该数组的生存期内不能更改。
速度通常并不是问题,如果有问题,你保存和检索对象的方式也很少是罪魁祸首。你应该总是从 ArrayList 开始,它将数组封装起来。必要时,它会自动分配更多的数组空间,创建新数组,并将旧数组中的引用移动到新数组。这种灵活性需要开销,所以一个 ArrayList 的效率不如数组。在极少的情况下效率会成为问题,所以这种时候你可以直接使用数组。
数组和集合(Collections)都不能滥用。不管你使用数组还是集合,如果你越界,你都会得到一个 RuntimeException 的异常提醒,这表明你的程序中存在错误。
在泛型前,其他的集合类以一种宽泛的方式处理对象(就好像它们没有特定类型一样)。事实上,这些集合类把保存对象的类型默认为 Object,也就是 Java 中所有类的基类。而数组是优于 预泛型 (pre-generic)集合类的,因为你创建一个数组就可以保存特定类型的数据。这意味着你获得了一个编译时的类型检查,而这可以防止你插入错误的数据类型,或者搞错你正在提取的数据类型。
当然,不管在编译时还是运行时,Java都会阻止你犯向对象发送不正确消息的错误。然而不管怎样,使用数组都不会有更大的风险。比较好的地方在于,如果编译器报错,最终的用户更容易理解抛出异常的含义。
一个数组可以保存基本数据类型,而一个预泛型的集合不可以。然而对于泛型而言,集合可以指定和检查他们保存对象的类型,而通过 自动装箱 (autoboxing)机制,集合表现地就像它们可以保存基本数据类型一样,因为这种转换是自动的。
数组和 ArrayList 之间的相似是设计者有意为之,所以在概念上,两者很容易切换。集合的功能明显多于数组。随着 Java 自动装箱技术的出现,通过集合使用基本数据类型几乎和通过数组一样简单。数组唯一剩下的优势就是效率。然而,当你解决一个更加普遍的问题时,数组可能限制太多,这种情形下,您可以使用集合类。
4 Arrays工具类
java.util.Arrays 该类包含许多其他有用的 静态 程序方法,我们将对此进行研究。
概述:
- asList(): 获取任何序列或数组,并将其转换为一个 列表集合 (集合章节介绍了此方法)。
- copyOf():以新的长度创建现有数组的新副本。
- copyOfRange():创建现有数组的一部分的新副本。
- equals():比较两个数组是否相等。
- deepEquals():多维数组的相等性比较。
- stream():生成数组元素的流。
- hashCode():生成数组的哈希值(您将在附录中了解这意味着什么:理解equals()和hashCode())。
- deepHashCode(): 多维数组的哈希值。
- sort():排序数组
- parallelSort():对数组进行并行排序,以提高速度。
- binarySearch():在已排序的数组中查找元素。
- parallelPrefix():使用提供的函数并行累积(以获得速度)。基本上,就是数组的reduce()。
- spliterator():从数组中产生一个Spliterator;这是本书没有涉及到的流的高级部分。
- toString():为数组生成一个字符串表示。你在整个章节中经常看到这种用法。
- deepToString():为多维数组生成一个字符串。你在整个章节中经常看到这种用法。对于所有基本类型和对象,所有这些方法都是重载的。
5 数组拷贝
在Java中,允许将一个数组变量拷贝给另一个数组变量。与使用for循环手工执行复制相比,copyOf() 和 copyOfRange() 复制数组要快得多。这些方法被重载以处理所有类型。
public class ArrayCopyingCase {
public static void main(String[] args) {
int[] a1 = new int[10];
a1[5] = 19;
int[] a2 = Arrays.copyOf(a1, a1.length);
int[] a3 = Arrays.copyOfRange(a1, 4, a1.length);
}
}
- copyOf() 和 copyOfRange() 也可以使用包装类型。copyOfRange() 需要一个开始和结束索引。
- 如果数组元素是数值型,那么多余的元素将被赋值为0;如果数组元素是布尔型,则将赋值为false。相反,如果长度小于原始数组的长度,则只拷贝最前面的数据元素。
6 数组比较
数组 提供了 equals() 来比较一维数组,以及 deepEquals() 来比较多维数组。对于所有原生类型和对象,这些方法都是重载的。
数组相等的含义:数组必须有相同数量的元素,并且每个元素必须与另一个数组中的对应元素相等,对每个元素使用 equals()(对于原生类型,使用原生类型的包装类的 equals() 方法;例如,int的Integer.equals()。
7 数组排序
根据对象的实际类型执行比较排序。一种方法是为不同的类型编写对应的排序方法,但是这样的代码不能复用。
编程设计的一个主要目标是“将易变的元素与稳定的元素分开”,在这里,保持不变的代码是一般的排序算法,但是变化的是对象的比较方式。因此,使用策略设计模式而不是将比较代码放入许多不同的排序源码中。使用策略模式时,变化的代码部分被封装在一个单独的类(策略对象)中。
您将一个策略对象交给相同的代码,该代码使用策略模式来实现其算法。通过这种方式,您将使用相同的排序代码,使不同的对象表达不同的比较方式。
Java有两种方式提供比较功能。第一种方法是通过实现 java.lang.Comparable 接口的原生方法。这是一个简单的接口,只含有一个方法 compareTo()。该方法接受另一个与参数类型相同的对象作为参数,如果当前对象小于参数,则产生一个负值;如果参数相等,则产生零值;如果当前对象大于参数,则产生一个正值。第二种使用内置的排序方法Arrays.sort(),您可以对实现了 Comparable 接口或具有Comparator 的任何对象数组 或 任何原生数组进行排序。这个方法使用了优化的快速排序算法。快速排序算法对于大多数数据集合来说都是效率比较高。
8 多维数组
要创建多维的基元数组,你要用大括号来界定数组中的向量:
int[][] a = {
{ 1, 2, 3, },
{ 4, 5, 6, },
};
每个嵌套的大括号都代表了数组的一个维度。你也可以使用 new 分配数组。这是一个使用 new 表达式分配的三维数组:
int[][][] a = new int[2][2][4];
9 总结
Java为固定大小的低级数组提供了合理的支持。这种数组强调的是性能而不是灵活性,就像C和c++数组模型一样。在Java的最初版本中,固定大小的低级数组是绝对必要的,这不仅是因为Java设计人员选择包含原生类型(也考虑到性能),还因为那个版本对集合的支持非常少。因此,在早期的Java版本中,选择数组总是合理的。
在Java的后续版本中,集合支持得到了显著的改进,现在集合在除性能外的所有方面都优于数组,即使这样,集合的性能也得到了显著的改进。正如本书其他部分所述,无论如何,性能问题通常不会出现在您设想的地方。在使用Java的最新版本进行编程时,应该“优先选择集合而不是数组”。只有当您证明性能是一个问题(并且切换到一个数组实际上会有很大的不同)时,才应该重构到数组。集合几乎总是更好的选择。
最后的最后
为初学者提供学习指南,为从业者提供参考价值。我坚信码农也具有产生洞见的能力。关注【码农洞见】,一起学习和交流吧!
猜你喜欢
- 2024-10-08 数组作为容器底层的数据结构,还是了解一下吧
- 2024-10-08 “全栈2019”Java原子操作第九章:atomic包下原子数组介绍与使用
- 2024-10-08 04、数组的注意事项 #Java(数组函数的使用方法)
- 2024-10-08 Java中的数组使用(java中数组用法)
- 2024-10-08 C++|实例解析函数指针数组的声明、初始化和使用
- 2024-10-08 小高分享(47)Java中的基本数据类型与数组
- 2024-10-08 定义结构体数组并初始化,定义结构体数组struct stu
- 2024-10-08 ArrayList初始化-Java那些事儿(java arraylist用法)
- 2024-10-08 看完这篇,轻松弄懂STM32 C语言变量的定义和初始化
- 2024-10-08 Java编程基础阶段笔记 day05 数组
你 发表评论:
欢迎- 最近发表
-
- pyinstaller打包python程序高级技巧
- 将python打包成exe的方式(python打包成exe的方法)
- Python打包:如何将 Flask 项目打包成exe程序
- py2exe实现python文件打包为.exe可执行程序(上篇)
- 如何将 Python 项目打包成 exe,另带卸载功能!
- Python打包成 exe,太大了该怎么解决?
- 可视化 Python 打包 exe,这个神器绝了!
- 案例详解pyinstaller将python程序打包为可执行文件exe
- Cocos 3.x 菜鸟一起玩:打包window程序
- 怎么把 Python + Flet 开发的程序,打包为 exe ?这个方法很简单!
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)