java collection framework
Sebastian Lv4

之前有过一篇集合框架的小结,随着时间的推移和使用体验,越发觉得那篇过于浅显,今天换个视角来看:如果是你,你会怎么设计?

换身份了

​ 作为jdk的设计者,我希望推广我的作品–JDK,方便用户使用,得有一些拿来即用的产品,日常的操作离不开数据处理,数据需要有地方存,就叫容器吧。我的jdk有基础数据类型,这些不方便操作,不符合面向对象,于是有了他们的包装类,但是不管什么是什么对象,都是继承于Object的,我可以用语法糖来做编译检查,这也是提高用户体验的一种方式,所以我的容器得用范型。emmm,我的idea就这么产生了!下面得考虑容器的内部设计了!

容器的种类

​ 装数据?拿什么装?我有数组,只能拿这个装,底层实现都是它,我把它封装起来好了。那用户可能有什么操作呢,增删改查,就这四个,但仔细想,还可以扩充出许多,例如,增,我可以在已有的后面追加,也可以第一个或中间任意一个地方加,其他的三个操作同理,这怎么搞?(好麻烦,不当设计者了吧?)现在就有了问题,细节怎么处理,是不是一开始考虑的太细节了?我把它作为一个作品、一个艺术品,艺术来源于生活,我都叫他容器了,想想生活中有什么器皿。

​ 银行柜员会挨个办理业务,这是一种读取数据的方式,往罐子里装食物,装完我先吃的都是后进去的,这也是一种……根据这个来做那成品岂不是容易理解,使用起来就不会陌生。我也是站在巨人的肩膀上,除了普通的类,还有接口和抽象类来使用,另外,参照生活中的“容器”,可以更好的使用面向对象。首先,无论是哪种容器,都得装东西吧,有装就有一些基本操作,先不考虑各种容器的差异,都会有大小、查找、是否包含、清空、返回数组(毕竟只有数组可以装东西了)等操作,那我就叫他Collection吧,规划蓝图,collection以后定会有不同的容器,难不成以后各个容器遍历的时候都要写一遍?我这个总接口都写好了,能否提供一个操作,不论里面装的啥、不论是哪种容器,使用这个方法都能把里面的东西挨个读出来,也就是说在某个地方写一个for循环就好了,于是我做了一个iterator接口出来,让collection继承了iterator。接口算是好了,我们需要让他逐渐具体。现在只有未实现的接口,想想有点像设计院的图纸,要出成品得交给工厂去做,而这个图纸目前来说就寥寥几笔,是一个蓝图的开始,需要再完善些。如何完善细节呢,要做哪些容器呢,还不知道,因为太多了,那我们就只具体一点点吧,不是还有抽象类么,没必要一步到位,所以找出通用的能实现的部分。因为有了iterator,所以可以做些事情了,例如,清空操作、定义实例变量等:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public abstract Iterator<E> iterator();

public abstract int size();

public void clear() {
Iterator<E> it = iterator();
while (it.hasNext()) {
it.next();
it.remove();
}
}

public Object[] toArray() {
// Estimate size of array; be prepared to see more or fewer elements
Object[] r = new Object[size()];
Iterator<E> it = iterator();
for (int i = 0; i < r.length; i++) {
if (! it.hasNext()) // fewer elements than expected
return Arrays.copyOf(r, i);
r[i] = it.next();
}
return it.hasNext() ? finishToArray(r, it) : r;
}

public boolean add(E e) {
throw new UnsupportedOperationException();
}

我已经实现了不少操作,是时候可以进一步了,根据元素的进出方式我觉得可以分为顺序进出、先进后出,又可以进一步以能否重复做划分。那么就有个问题,我设计collection接口的时候有些点没有想到,比如有序的容器我如果直接找出第几个元素怎么办?当时没做这个接口,没提供这个操作诶?是不是设计的有问题?其实不是,接口不也可以继承,本来就不是面面具到,就得一步步地完成抽象、进而具体的,我们可以继承collection接口去完善他,做出我们想要的容器的接口,因为继承,所以之前的操作都还在,他们是通用的。按照这个想法,可以有ListSetQueue接口。有了接口,又回到刚才考虑Collection接口的点了,先用抽象类尽可能地实现接口,但同时要注意,我们已经实现过了Collection,所以要避免重复实现,我们的接口也是集成了Collection,这个时候我们具体容器的抽象类得集成Collection的抽象类。除了装成一堆的容器,也得有一对一的吧?比如商店售货员,给他一种商品价格,他得知道价格是多少,这就是一对一的关系,鉴于次,不同与collection的容器又被抽象出来了:Map(于是又要考虑一大堆并且设计接口、完成抽象类……)。

jdk就是这样,一步一步地从功能、特性考虑对现实世界的物品建模,封装成我们能用的数据结构。

设计规律

(好了 身份换回来了)集合框架给了我们典型的、优秀的设计案例:如何抽象、如何一步步地封装、如何变得具体。要学的不仅仅是如何使用,更重要的是思想。