💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
## 数组特性 明明还有很多其他的办法来保存对象,那么是什么令数组如此特别? 将数组和其他类型的集合区分开来的原因有三:效率,类型,保存基本数据类型的能力。在 Java 中,使用数组存储和随机访问对象引用序列是非常高效的。数组是简单的线性序列,这使得对元素的访问变得非常快。然而这种高速也是有代价的,代价就是数组对象的大小是固定的,且在该数组的生存期内不能更改。 速度通常并不是问题,如果有问题,你保存和检索对象的方式也很少是罪魁祸首。你应该总是从 **ArrayList** (来自 [集合](book/12-Collections.md ))开始,它将数组封装起来。必要时,它会自动分配更多的数组空间,创建新数组,并将旧数组中的引用移动到新数组。这种灵活性需要开销,所以一个 **ArrayList** 的效率不如数组。在极少的情况下效率会成为问题,所以这种时候你可以直接使用数组。 数组和集合(Collections)都不能滥用。不管你使用数组还是集合,如果你越界,你都会得到一个 **RuntimeException** 的异常提醒,这表明你的程序中存在错误。 在泛型前,其他的集合类以一种宽泛的方式处理对象(就好像它们没有特定类型一样)。事实上,这些集合类把保存对象的类型默认为 **Object**,也就是 Java 中所有类的基类。而数组是优于 **预泛型** (pre-generic)集合类的,因为你创建一个数组就可以保存特定类型的数据。这意味着你获得了一个编译时的类型检查,而这可以防止你插入错误的数据类型,或者搞错你正在提取的数据类型。 当然,不管在编译时还是运行时,Java都会阻止你犯向对象发送不正确消息的错误。然而不管怎样,使用数组都不会有更大的风险。比较好的地方在于,如果编译器报错,最终的用户更容易理解抛出异常的含义。 一个数组可以保存基本数据类型,而一个预泛型的集合不可以。然而对于泛型而言,集合可以指定和检查他们保存对象的类型,而通过 **自动装箱** (autoboxing)机制,集合表现地就像它们可以保存基本数据类型一样,因为这种转换是自动的。 下面给出一例用于比较数组和泛型集合: ```java // arrays/CollectionComparison.java // (c)2017 MindView LLC: see Copyright.txt // We make no guarantees that this code is fit for any purpose. // Visit http://OnJava8.com for more book information. import java.util.*; import onjava.*; import static onjava.ArrayShow.*; class BerylliumSphere { private static long counter; private final long id = counter++; @Override public String toString() { return "Sphere " + id; } } public class CollectionComparison { public static void main(String[] args) { BerylliumSphere[] spheres = new BerylliumSphere[10]; for(int i = 0; i < 5; i++) spheres[i] = new BerylliumSphere(); show(spheres); System.out.println(spheres[4]); List<BerylliumSphere> sphereList = Suppliers.create( ArrayList::new, BerylliumSphere::new, 5); System.out.println(sphereList); System.out.println(sphereList.get(4)); int[] integers = { 0, 1, 2, 3, 4, 5 }; show(integers); System.out.println(integers[4]); List<Integer> intList = new ArrayList<>( Arrays.asList(0, 1, 2, 3, 4, 5)); intList.add(97); System.out.println(intList); System.out.println(intList.get(4)); } } /* Output: [Sphere 0, Sphere 1, Sphere 2, Sphere 3, Sphere 4, null, null, null, null, null] Sphere 4 [Sphere 5, Sphere 6, Sphere 7, Sphere 8, Sphere 9] Sphere 9 [0, 1, 2, 3, 4, 5] 4 [0, 1, 2, 3, 4, 5, 97] 4 */ ``` **Suppliers.create()** 方法在[泛型](book/20-Generics.md)一章中被定义。上面两种保存对象的方式都是有类型检查的,唯一比较明显的区别就是数组使用 **[ ]** 来随机存取元素,而一个 **List** 使用诸如 `add()` 和 `get()` 等方法。数组和 **ArrayList** 之间的相似是设计者有意为之,所以在概念上,两者很容易切换。但是就像你在[集合](book/12-Collections.md)中看到的,集合的功能明显多于数组。随着 Java 自动装箱技术的出现,通过集合使用基本数据类型几乎和通过数组一样简单。数组唯一剩下的优势就是效率。然而,当你解决一个更加普遍的问题时,数组可能限制太多,这种情形下,您可以使用集合类。 ### 用于显示数组的实用程序 在本章中,我们处处都要显示数组。Java 提供了 **Arrays.toString()** 来将数组转换为可读字符串,然后可以在控制台上显示。然而这种方式视觉上噪音太大,所以我们创建一个小的库来完成这项工作。 ```java // onjava/ArrayShow.java // (c)2017 MindView LLC: see Copyright.txt // We make no guarantees that this code is fit for any purpose. // Visit http://OnJava8.com for more book information. package onjava; import java.util.*; public interface ArrayShow { static void show(Object[] a) { System.out.println(Arrays.toString(a)); } static void show(boolean[] a) { System.out.println(Arrays.toString(a)); } static void show(byte[] a) { System.out.println(Arrays.toString(a)); } static void show(char[] a) { System.out.println(Arrays.toString(a)); } static void show(short[] a) { System.out.println(Arrays.toString(a)); } static void show(int[] a) { System.out.println(Arrays.toString(a)); } static void show(long[] a) { System.out.println(Arrays.toString(a)); } static void show(float[] a) { System.out.println(Arrays.toString(a)); } static void show(double[] a) { System.out.println(Arrays.toString(a)); } // Start with a description: static void show(String info, Object[] a) { System.out.print(info + ": "); show(a); } static void show(String info, boolean[] a) { System.out.print(info + ": "); show(a); } static void show(String info, byte[] a) { System.out.print(info + ": "); show(a); } static void show(String info, char[] a) { System.out.print(info + ": "); show(a); } static void show(String info, short[] a) { System.out.print(info + ": "); show(a); } static void show(String info, int[] a) { System.out.print(info + ": "); show(a); } static void show(String info, long[] a) { System.out.print(info + ": "); show(a); } static void show(String info, float[] a) { System.out.print(info + ": "); show(a); } static void show(String info, double[] a) { System.out.print(info + ": "); show(a); } } ``` 第一个方法适用于对象数组,包括那些包装基本数据类型的数组。所有的方法重载对于不同的数据类型是必要的。 第二组重载方法可以让你显示带有信息 **字符串** 前缀的数组。 为了简单起见,你通常可以静态地导入它们。