集合类是我们每天都在使用的工具。工欲善其事,必先利其器。下面我们从源码的角度来看看集合类中的奥秘。
文章目录
1. 整体
下面图片是整体的架构,这里只表现了类和接口类的继承实现关系,选取了平时使用比较多的类,如LinkedList、ArrayList、HashMap、TreeMap等,没有考虑并发容器。集合类分为两个体系,Collection和Map。我们先看Collection。
1)Collection
从上往下看,非常清晰,Collection实现了Iterable接口,也就是迭代器接口,我们后面再说。Collection有三个子接口,分别是List、Queue、Set。Collection、List、Queue、Set都有自己对应的抽象类。这里我们遇到了经典的问题,接口和抽象类的区别是什么?
抽象类是为了代码复用,将子类的共同方法组织到抽象类中,减少重复代码,抽象方法可以使结构上更加清晰,还可以使用多态的特性。
接口更注重的是行为抽象,降低代码之间的耦合性,提高可拓展性。从整体来讲,可以使调用者关注接口,而不是具体实现。
从开发角度来说,我们发现子类的代码重复,就会抽象出抽象父类来减少重复代码。而接口则是我们在设计系统时先确定好的,之后再考虑具体实现。
接下来就是一些中间接口。最下面就是我们平时常用的具体类。
这些类还实现了一些特殊的接口,我们后面再看。
2)Map
Map和Collection大同小异,这里只放一下整体架构,大家有个印象就好,后面我们详细再说。
2. Iterable
首先看一下接口定义。Iterable接口表示一种能力,具有迭代的能力。Iterable接口中有三个方法,其余两个和本文的内容无关,我们忽略。iterator方法用于返回一个迭代器,在各个容器中有各自的实现。真正的迭代器类是Iterator接口,其中定义了迭代器的基本操作,在各个容器中也有具体的迭代器实现,在后面的文章再具体分析。
public interface Iterable<T> {
Iterator<T> iterator();
// ...
}
public interface Iterator<E> {
boolean hasNext();
E next();
default void remove() {
throw new UnsupportedOperationException("remove");
}
// ...
}
3. RandomAccess、Serializable、Cloneable
从整体图中我们可以看到Cloneable、Serializable、 RandomAccess三个接口出场率很高。我们经常使用的类中,都至少实现了这三个接口中的一个。
翻看他们的定义,我们发现了一个共同的特征,三个接口都是空的!!
这种接口叫做标志接口,他不定义任何操作,只是起到一种标记作用。
public interface RandomAccess {
}
public interface Serializable {
}
public interface Cloneable {
}
下面我们看几个例子,体会一下。
1)RandomAccess
RandomAccess的意思是快速随机访问,官网中有解释。下面是Java Collections工具类中二分查找的实现,可以看到,如果容器支持快速随机访问,则会使用基于索引的二分查找,否则使用基于迭代的二分查找。
// java.util.Collections
public static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key) {
if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
return Collections.indexedBinarySearch(list, key);
else
return Collections.iteratorBinarySearch(list, key);
}
2)Serializable
Serializable标志一个类可以被序列化,数据在网络中传送是串行的,所以在网络中发送数据必须要进行序列化,接受数据要进行反序列化。下面的方法中,如果需要被序列化的对象没有实现Serializable接口,则会抛出NotSerializableException异常。
// java.io.ObjectOutputStream
/*** @throws NotSerializableException Some object to be serialized does not
* implement the java.io.Serializable interface.
*/
public final void writeObject(Object obj) throws IOException {
if (enableOverride) {
writeObjectOverride(obj);
return;
}
try {
writeObject0(obj, false);
} catch (IOException ex) {
if (depth == 0) {
writeFatalException(ex);
}
throw ex;
}
}
3)Cloneable
祖宗类Object有个方法 clone(),他和Cloneable一起合作,用来实现Java中的克隆。使用场景参考不变模式如何实现不变性,第三。下面是一个简单的栗子。
class Student implements Cloneable{
private int number;
@Override
public Object clone() {
Student stu = null;
try{
stu = (Student)super.clone();
}catch(CloneNotSupportedException e) {
e.printStackTrace();
}
return stu;
}
}