Arrays
At the end of the Initialization amp; Cleanup chapter, you learned how to define and initialize an array.
The simple view of arrays is that you create and populate them, you select elements from them using int indexes, and they donrsquo;t change their size. Most of the time thatrsquo;s all you need to know, but sometimes you need to perform more sophisticated operations on arrays, and you may also need to evaluate the use of an array vs. a more flexible container. This chapter will show you how to think about arrays in more depth.
Why arrays are special
There are a number of other ways to hold objects, so what makes an array special?
There are three issues that distinguish arrays from other types of containers: efficiency, type, and the ability to hold primitives. The array is Javarsquo;s most efficient way to store and
randomly access a sequence of object references. The array is a simple linear sequence, which makes element access fast. The cost of this speed is that the size of an array object is fixed
and cannot be changed for the lifetime of that array. You might suggest an ArrayList (from Holding Your Objects), which will automatically allocate more space, creating a new one and moving all the references from the old one to the new one. Although you should generally prefer an ArrayList to an array, this flexibility has overhead, so an ArrayList is measurably
less efficient than an array.
Both arrays and containers guarantee that you canrsquo;t abuse them. Whether yoursquo;re using an array or a container, yoursquo;ll get a RuntimeException if you exceed the bounds, indicating a programmer error.
Before generics, the other container classes dealt with objects as if they had no specific type. That is, they treated them as type Object, the root class of all classes in Java. Arrays are superior to pre-generic containers because you create an array to hold a specific type. This means that you get compile-time type checking to prevent you from inserting the wrong type or mistaking the type that yoursquo;re extracting. Of course, Java will prevent you from sending an inappropriate message to an object at either compile time or run time. So itrsquo;s not riskier one way or the other; itrsquo;s just nicer if the compiler points it out to you, and therersquo;s less likelihood that the end user will get surprised by an exception.
An array can hold primitives, whereas a pre-generic container could not. With generics, however, containers can specify and check the type of objects they hold, and with autoboxing containers can act as if they are able to hold primitives, since the conversion is automatic. Herersquo;s an example that compares arrays with generic containers:
//: arrays/ContainerComparison.java import java.util.*;
import static net.mindview.util.Print.*;
class BerylliumSphere {
private static long counter;
private final long id = counter ;
public String toString() { return 'Sphere ' id; }
}
public class ContainerComparison {
public static void main(String[] args) { BerylliumSphere[] spheres = new BerylliumSphere[10]; for(int i = 0; i lt; 5; i )
spheres[i] = new BerylliumSphere();
print(Arrays.toString(spheres));
print(spheres[4]);
Listlt;BerylliumSpheregt; sphereList =
new ArrayListlt;BerylliumSpheregt;();
for(int i = 0; i lt; 5; i )
sphereList.add(new BerylliumSphere());
print(sphereList);
print(sphereList.get(4));
int[] integers = { 0, 1, 2, 3, 4, 5 }; print(Arrays.toString(integers)); print(integers[4]);
Listlt;Integergt; intList = new ArrayListlt;Integergt;( Arrays.asList(0, 1, 2, 3, 4, 5));
intList.add(97);
print(intList);
print(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
*///:~
Both ways of holding objects are type-checked, and the only apparent difference is that arrays use [ ] for accessing elements, and a List uses methods such as add( ) and get( ). The similarity between arrays and the ArrayList is intentional, so that itrsquo;s conceptually easy to switch between the two. But as you saw in the Holding Your Objects chapter, containers have significantly more functionality than arrays.
With the advent of autoboxing, containers are nearly as easy to use for primitives as arrays. The only remaining advantage to arrays is efficiency. However, when yoursquo;re solving a more general problem, arrays can be too restrictive, and in those cases you use a container class.
Arrays are first-class objects
Regardless of what type of array yoursquo;re working with, the array identifier is actually a reference to a true object thatrsquo;s created on the heap. This is the object that holds the references to the other objects, and it can be created either implicitly, as part of the array initialization syntax, or explicitly with a new expression. Part of the array object (in fact, the only field or method you can access) is the read-only length member that tells you how
many elements can be stored in that array object. The lsquo;[ ]rsquo; syntax is the only other access that you have to the array object.
The following example summarizes the various ways that an array can be initialized, and how the array references can be assigned to different array objects. It also shows that arrays of
objects and arrays of primitives are almost identical in their use. The only difference is that arrays of obje
全文共58732字,剩余内容已隐藏,支付完成后下载完整资料
第 16章 对象的容纳
“如果一个程序只含有数量固定的对象,而且已知它们的存在时间,那么这个程序可以说是相当简单的。”
通常,我们的程序需要根据程序运行时才知道的一些标准创建新对象。若非程序正式运行,否则我们根本不知道自己到底需要多少数量的对象,甚至不知道它们的准确类型。为了满足常规编程的需要,我们要求能在任何时候、任何地点创建任意数量的对象。所以不可依赖一个已命名的句柄来容纳自己的每一个对象,就象下面这样:
MyObject myHandle;
因为根本不知道自己实际需要多少这样的东西。为解决这个非常关键的问题,Java 提供了容纳对象(或者对象的句柄)的多种方式。其中内建的类型是数组,我们之前已讨论过它,本章准备加深大家对它的认识。此外,Java 的工具(实用程序)库提供了一些 “集合类”(亦称作“容器类”,但该术语已由 AWT 使用,所以这里仍采用“集合”这一称呼)。利用这些 集合类,我们可以容纳乃至操纵自己的对象。本章的剩余部分会就此进行详细讨论。
16.1 数组
对数组的大多数必要的介绍已在第 4 章的最后一节进行。通过那里的学习,大家已知道自己该如何定义及初 始化一个数组。对象的容纳是本章的重点,而数组只是容纳对象的一种方式。但由于还有其他大量方法可容 纳数组,所以是哪些地方使数组显得如此特别呢? 有两方面的问题将数组与其他集合类型区分开来:效率和类型。对于 Java 来说,为保存和访问一系列对象(实际是对象的句柄)数组,最有效的方法莫过于数组。数组实际代表一个简单的线性序列,它使得元素的访问速度非常快,但我们却要为这种速度付出代价:创建一个数组对象时,它的大小是固定的,而且不可在 那个数组对象的“存在时间”内发生改变。可创建特定大小的一个数组,然后假如用光了存储空间,就再创建一个新数组,将所有句柄从旧数组移到新数组。这属于“矢量”(Vector)类的行为,本章稍后还会详细 讨论它。然而,由于为这种大小的灵活性要付出较大的代价,所以我们认为矢量的效率并没有数组高。 C 的矢量类知道自己容纳的是什么类型的对象,但同 Java 的数组相比,它却有一个明显的缺点:C 矢量类 的 operator[]不能进行范围检查,所以很容易超出边界(然而,它可以查询 vector 有多大,而且 at() 方法 确实能进行范围检查)。在 Java 中,无论使用的是数组还是集合,都会进行范围检查——若超过边界,就会 获得一个 RuntimeException(运行期违例)错误。正如大家在第 9 章会学到的那样,这类违例指出的是一个 程序员错误,所以不需要在代码中检查它。在另一方面,由于 C 的 vector 不进行范围检查,所以访问速度 较快——在 Java 中,由于对数组和集合都要进行范围检查,所以对性能有一定的影响。 本章还要学习另外几种常见的集合类:Vector(矢量)、Stack(堆栈)以及 Hashtable(散列表)。这些类 都涉及对对象的处理——好象它们没有特定的类型。换言之,它们将其当作 Object 类型处理(Object 类型是 Java 中所有类的“根”类)。从某个角度看,这种处理方法是非常合理的:我们仅需构建一个集合,然后 任何 Java 对象都可以进入那个集合(除基本数据类型外——可用 Java 的基本类型封装类将其作为常数置入 集合,或者将其封装到自己的类内,作为可以变化的值使用)。这再一次反映了数组优于常规集合:创建一 个数组时,可令其容纳一种特定的类型。这意味着可进行编译期类型检查,预防自己设置了错误的类型,或 者错误指定了准备提取的类型。当然,在编译期或者运行期,Java 会防止我们将不当的消息发给一个对象。 所以我们不必考虑自己的哪种做法更加危险,只要编译器能及时地指出错误,同时在运行期间加快速度,目 的也就达到了。此外,用户很少会对一次违例事件感到非常惊讶的。 考虑到执行效率和类型检查,应尽可能地采用数组。然而,当我们试图解决一个更常规的问题时,数组的局 限也可能显得非常明显。在研究过数组以后,本章剩余的部分将把重点放到 Java 提供的集合类身上。
16.1.1 数组和第一类对象
无论使用的数组属于什么类型,数组标识符实际都是指向真实对象的一个句柄。那些对象本身是在内存 “堆”里创建的。堆对象既可“隐式”创建(即默认产生),亦可“显式”创建(即明确指定,用一个 new 表达式)。堆对象的一部分(实际是我们能访问的唯一字段或方法)是只读的 length(长度)成员,它告诉 我们那个数组对象里最多能容纳多少元素。对于数组对象,“[]”语法是我们能采用的唯一另类访问方法。 下面这个例子展示了对数组进行初始化的不同方式,以及如何将数组句柄分配给不同的数组对象。它也揭示出对象数组和基本数据类型数组在使用方法上几乎是完全一致的。唯一的差别在于对象数组容纳的是句柄, 而基本数据类型数组容纳的是具体的数值(若在执行此程序时遇到困难,请参考第 3 章的“赋值”小节):
//: ArraySize.java
//Initialization amp; re -assignment of arrays package c08;
class Weeble {}// A small mythical creature
public class ArraySize {
public static void main(String[]args){
// Arrays of objects: Weeble[] a; // Null handle
Weeble[] b = new Weeble[5]; // Null handles
Weeble[] c = new Weeble[4];
for(int i = 0; i lt; c.length; i )
c[i] = new Weeble();
Weeble[] d = {new Weeble(), new Weeble(), new Weeble()};
// Compile error: variable a not initialized:
//!System.out.println('a.length=' a.length);
System.out.println('b.length = ' b.length);
// The handles inside the array are
// automatically initialized to null:
for(int i = 0; i lt; b.length; i )
System.out.println('b[' i ']=' b[i]);
System.out.println('c.length = ' c.length);
System.out.pri ntln('d.length = ' d.length);
a = d;
System.out.println('a.length = ' a.length);
// Java 1.1 initialization syntax:
a = new Weeble[] {
new Weeble(), new Weeble()
};
System.out.println('a.length = ' a.length);
// Arrays of primitives: int[] e; // Null handle int[] f = new int[5]; int[] g = new int[4];
for(int i = 0; i lt; g.length; i )
g[i] = i*i;
int[] h = { 11, 47, 93 };
// Compile error: variable e not initialized:
//!System.out.println('e.length=' e.length);
System.out.println('f.length = ' f.length);
// The primitives inside the array are
// automatically initialized to zero:
for(int i = 0; i lt; f.length; i )
System.out.println('f[' i ']=' f[i]);
System.out.println('g.length = ' g.length);
System.out.println('h.length = ' h.length);
e = h;
System.out.println('e.length = ' e.length);
// Java 1.1 initialization syntax:
e = new int[] { 1, 2 };
System.out.println('e.lengt h = ' e.length);
}
} ///:~
Herersquo;s the output from the program:
b.length = 5
b[0]=null b[1]=null b[2]=null b[3]=null b[4]=null
c.length = 4 d.length = 3 a.length = 3 a.length = 2 f.length = 5 f[0]=0
f[1]=0 f[2]=0 f[3]=0 f[4]=0
g.length = 4 h.length = 3 e.length = 3 e.length = 2
其中,数组 a 只是初始化成一个 null 句柄。此时,编译器会禁止我们对这个句柄作任何实际操作,除非已正 确地初始化了它。数组 b 被初始化成指向由 Weeble 句柄构成的一个数组,但那个数组里实际并未放置任何 Weeble 对象。然而,我们仍然可以查询那个数组的大小,因为 b 指向的是一个合法对象。这也为我们带来了 一个难题:不可知道那个数组里实际包含了多少个元素,因为 length 只告诉我们可将多少元素置入那个数 组。换言之,我们只知道数组对象的大小或容量,不知其实际容纳了多少个元素。尽管如此,由于数组对象 在创建之初会自动初始化成 null ,所以可检查它是否为 null ,判断一个特定的数组“空位”是否容纳一个对 象。类似地,由基本数据类型构成的数组会自动初始化成零(针对数值类型)、null (字符类型)或者 false(布尔类型)。
数组 c 显示出我们首先创建一个数组对象,再将 Weeble 对象赋给那个数组的所有“空
全文共82976字,剩余内容已隐藏,支付完成后下载完整资料
资料编号:[11583],资料为PDF文档或Word文档,PDF文档可免费转换为Word
以上是毕业论文外文翻译,课题毕业论文、任务书、文献综述、开题报告、程序设计、图纸设计等资料可联系客服协助查找。