通常来说,如果不知道在解决某个特定问题需要多少对象或者他们将存活多久时,那么就不可能知道如何存储这些对象。如何才能知道需要多少空间来创建这些对象呢?答案是你不可能知道(老外真会玩),因为这类信息只有在运行时才能获得。
对于面向对象程序设计中的大多数问题而言,这个问题的解决方案似乎过于轻率:创建另一种对象类型。这种新的对象类型持有对其他对象的引用。当然,你可以用在大多数语言中都有的数组类型来实现相同的功能。但是这个通常被称为容器(Java中被称为“集合”)的新对象,在任何需要时都可以扩充自己以容纳你置于其中的所有东西。因此不需要知道将来会把多少个对象置于容器中,只需要创建一个容器对象,然后让它处理所有细节。
幸运的是,好的OOP语言都有一组容器,它们作为开发包的一部分。在C++中,容器是标准C++类库的一部分,经常被称为标准模板类库(Standard emplate Library,STL)。Object Pascal在其可视化构件库(Visual Componet Library,VCL)中有容器;Smalltalk提供了一个非常完备的容器集;Java在其标准类库中也包含有大量的容器。在某些类库中,一两个通用容易足够满足所有需要;但是在其他类库(例如Java)中,具有满足不同需要的各种类型的容器,例如List(用于存储序列),Map(也被称为关联数组,用来建立对象之间的关联),Set(每种对象类型只持有一个),以及诸如队列、树、堆栈等更多的构件。
从设计的观点来看,真正需要的只是一个可以被操作从而解决问题的序列。如果单一类型的容器可以满足所有需求,那么就没有必要设计不同类型的序列了。然而还是需要对容器有所选择,这有两个原因。第一,不同容器提供了不同类型的接口和外部行为。堆栈相比于队列就具备不同的接口和行为,也不同于集合和列表的接口和行为。它们之中的某些容器提供的解决方案可能比其他容器要灵活的多。第二,不同的容器对于某些操作具有不同的效率。最好的例子就是两种List的比较:ArrayList和LinkedList。他们都是具有相同接口和外部行为的简单的序列,但是它们对某些操作所花费的代价却有天壤之别。在ArrayList中,随机访问元素是一个话费固定时间的操作;但是,对LinkedList来说,随机选取元素需要在列表中移动,这种代价是高昂的,访问越靠近表尾的元素,花费的是爱你越长。而另一方面,如果想在序列中插入一个元素,LinkedList的开销却比ArrayList要小。上述操作以及其他操作的效率,依序列底层结构的不同而存在很大的差异。我们可以在一开始使用LinkedList构建程序,而在优化系统性能是改用ArrayList。接口List所带来的抽象,把容器之间进行转换时对代码产生的影响降到了最小限度。
1.9.1参数化类型
在Java SE5出现之前,容器存储的对象都只具有Java中的通用类型:Object。单根继承结构意味着所有的东西都是Object类型,所以可以存储Object的容器可以存储任何东西。这使得容器很容易被复用。
要使用这样的容器,只需在其中置入对象的引用,稍后还可以将它们取回。但是由于容器只存储Object,所以当将对象引用置入容器时,它必须向上转型为Object,因此他会丢失其身份。当把它取回时,就获取了一个对Object对象的引用,而不是对置入是的哪个类型的对象的引用。所以怎样才能将它变回先前置入容器中是的具有实用接口的对象呢?
这里再度用到了转型,但是这一次不是向继承结构的上层转型为一个更泛化的类型,而是向下转型为一个更具体的类型。这种转型方式称为向下转型。我们知道,向上转型是安全的,例如Circle是一种Shape类型,但是不知道某个Object是Circle还是Shape,所以除非确切知道所要处理的对象的类型,否则向下转型几乎是不安全的。
然而向下转型并非是彻底危险的,因为如果向下转型为错误的类型,就会得到被称为异常的运行时错误,稍后会介绍什么是异常。尽管如此,当从容器中去除对象引用时,还是必须要以某种方式记住这些对象究竟是什么类型,这样才能执行正确的向下转型。
向下转型和运行时的检查需要额外的程序运行时间,也需要程序员付出更多的心血。那么创建正阳的容器,它知道自己所保存的对象的类型,从而不需要向下转型以及消除犯错误的可能,这样不是更有意义吗?这种解决方案被称为参数化类型机制。参数化类型就是一个编译器可以自动定制作用于特定类型上的类。例如,通过使用参数化类型,编译器可以定制一个只接纳和取出Shape对象的容器。
Java SE5的重大变化之一就是增加了参数化类型,在Java中它被称为泛型。一对尖括号,中间包含类型的信息,通过这些特征就可以识别对泛型的使用。例如,可以用下面这样的语句来创建一个存储Shape的ArrayList:
ArrayList<Shape> shapes = new ArrayList<Shape>();
为了利用泛型的优点,很多标准类库构件都进行了修改。泛型对程序设计中的很多代码都产生了重要的影响。
总结
- 对象应该存储什么位置,引入了“容器”(集合)
- 好多语言都有集合
- 根据需求设计不同类型的容器。一,不同容器提供了不同类型的接口和外部行为;二,不同的容器对于某些操作具有不同的效率
- ArrayList跟LinkedList的优缺点
- 因为往容器中存取对象需要转型的各种不便与不安全引入了“泛型”
----END----
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于