提交 f1d5b1e1 编写于 作者: A Allenyep

chap2

上级 e168cfa4
# 第二章
# 抽象数据类型
 本书中包含大多数课程中的经典数据结构,如一些具有代表性的数据集合框架。这意味着其中包括了一些可能使这些值变得有序值的集合或多元集合[1]。其中一些数据集合是关联索引的;它们是搜索结构,拥有类似于将某些索引值(键)映射到其他数据(例如将名称映射到街道地址)的函数。
 我们可以通过介绍不同数据结构的操作集来描述抽象的情况-即通过描述可用的抽象数据类型。从程序的角度来看,它需要某种数据集合用于操作,而这一系列操作方法必须是大家都应该知道的。
 对于每种不同的抽象数据类型,通常有几种可能的实现方式。选择哪个数据结构取决于你的程序需要处理多少数据,处理数据的速度有多快,以及对存储空间的限制。对于很多程序来说,这有点像一个肮脏的秘密交易,你选择实现的方式完全不重要。然而,知识丰富的程序员应该熟悉这些可用的工具。
 我希望你们中的许多人会发现这一章令人沮丧,因为它将谈论数据类型的*接口*,而不是谈论它们背后的实现方法。习惯它。毕竟,在这套标准库背后有广泛使用的编程语言将会以一组接口的方式呈现给你。包括:函数中参数传递的方向以及一些注释,通常是使用英语来描述接口的作用。作为一名开发者,您将花费大量时间来生成向您的客户提供相同功能的模块。
## 2.1 迭代器
 如果我们要开发一些数据集合的概念,我们必须回答这样一个问题:我们如何从这样的数据集合中获取对应项?你可能已经熟悉了一种集合结构——数组。获取其中元素十分简单。例如,要打印数组的内容,您可以编写
``` java
for (int i = 0; i < A.length; i += 1)
System.out.print (A[i] + ", ");
```
数组有n个元素,所以这样的循环很容易。但是其他集合呢?例如罐子里"第一个硬币"是哪一个呢?即使我们任意选择给一个集合中的每个元素一个编号,我们将会发现"选取第n个元素"代价将会十分昂贵(想象一下Scheme当中的lists)。
&emsp;尝试对每个集合强加编号以提取其中元素的问题在于它迫使集合的实现者提供比我们的问题可能需要的更加具体的工具。这是一个经典的工程权衡问题:满足一个约束(如获取第n个元素)可能会有其他代价(逐个迭代的代价十分高)。
&emsp;所以问题是在不依赖索引或者可能完全没有依赖顺序的情况下提供集合中的元素。Java提供了两种方法作为接口。接口**java.util.Iterator**提供了一种以某种顺序访问集合中所有项目的方法。**java.util.ListIterator**提供以某种特定顺序访问集合中的元素的方法,但没有为每个元素分配索引\[2\]
\[2\]:该库还定义了接口java.util.Enumeration,它本质上是同一想法的旧版本。我们不会在这里谈论这个界面,因为最重要的位置是新的程序首选的是迭代器
### 2.1.1 迭代器接口
&emsp;Java库定义了一个接口:**java.util.Iterator**,如代码2.1所示,表示为“在集合中所有元素排序的东西”的概念,但不保证顺序。这只是一个Java接口,它不具备实现类。在Java库中,代表数据项集合以提供对这些元素进行排序的方法的类的标准方法是定义诸如
``` java
Iterator<SomeType> iterator () { ... }
```
它分配并返回一个迭代器(如代码3.3)。通常这个迭代器的实际类型将被隐藏(甚至为private);所有使用这个类的用户都需要知道的是**iterator**返回的对象提供了两个方法**hasNext****next**(有时候是**remove**)。例如,打印字符串集合的所有元素的一般方法(类似于以前的阵列打印机)是:
``` java
for (Iterator<String> i = C.iterator ();i.hasNext (); )
System.out.print (i.next () + " ");
```
``` java
package java.util;
/** An object that delivers each item in some collection of items* each of which is a T. */
public interface Iterator <T> {
/** True iff there are more items to deliver. */
boolean hasNext ();
/** Advance THIS to the next item and return it. */
T next ();
/** Remove the last item delivered by next() from the collection* being iterated over. Optional operation: may throw* UnsupportedOperationException if removal is not possible. */
void remove ();
}
```
代码2.1 **java.util.Iterator**接口
&emsp;编写这个循环的程序员不需要知道对象i必须经历什么样的迭代以产生所请求的元素。即使代表其集合的C发生重大变化也不需要对循环进行修改。
&emsp;这种特殊类型的for循环在Java 2中是如此常见和有用,版本1.5中它有自己的“语法糖”,称为增强for循环。你可以编写如下代码
``` java
for (String i : C)
System.out.print (i + " ");
```
获得与前一个for循环相同的效果。Java将插入缺失的部分,将其转换为
``` java
for (Iterator<String> ρ = C.iterator (); ρ.hasNext(); ) {
String i = ρ.next ();
System.out.println (i + " ");
}
```
其中ρ是编译器引入的一些新变量,在程序的其他地方未使用,其类型取自**C.iterator()**。这个增强的for循环适用于任何取自对象接口**java.lang.Iterable**的对象C。简单地定义
``` java
public interface Iterable<T> {
Iterator<T> iterator ();
}
```
感谢增强for循环,只需在类型定义上定义迭代器方法,就提供了一种非常方便的方法来对序列中包含的该类型的任何子对象进行排序。
不用说,为迭代器(Iterators)引入了这种方便的简写,Java的设计者突然有这样一个观点:迭代遍历数组的元素比迭代遍历库类的更加笨拙。因此,他们将增强的for语句扩展为包含数组。例如,这两种方法是等价的:
``` java
/** The sum of the* elements of A */
int sum (int[] A) {
int S;S = 0;
for (int x : A)
S += x;
}
/** The sum of the elements* of A */
int sum (int[] A) {
int S;
S = 0;
for (int κ = 0; κ < A.length; κ++){
int x = A[κ];
S += x;
}
}
```
### 2.1.2 ListIterator接口
有些数据集合的确有排序的自然概念,但从按索引的集合中提取任意项的代价可能仍然很昂贵。例如,您可能看到了Scheme语言中的链表:给定列表中的一个元素,它需要n个操作来确定第n个后续元素(与Java数组相反,Java数组只需要一个步骤或一些特定操就可以检索任何项。)。标准Java库包含接口**java.util.ListIterator**,它通过有序序列进行排序的,而不通过索引来获取每个序列。如代码2.2所示。除了迭代器的“查询”方法和**remove**方法之外,**Listaterator**类提供了在集合中插入新项或替换项的操作。
## 2.2 Java集合抽象
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册