Java类库帮助我们在程序设计中实现传统的数据结构,统称为集合类。集合类的基本接口是Collection和Map(映射)。众多数据结构接口如List(列表),Set(集合),Queue(队列)等均继承于Collection接口。而这些接口又都有对应的抽象类实现接口中大部分方法。
我们先来说说这些数据结构各自的特征
List
有序,可重复,可通过索引获取元素,可以存在多个null。
Set
无序,不可重复,最多只能存在一个null。
Queue
队列的类型取决于对元素的排序规则。有FIFO(first-in-first-out),LIFO(last-in-first-out),priority queues(优先队列)。但无论是哪种规则,队列的头都会被remove()调用移除。所以在FIFO中新增的元素都会放在尾部,而在LIFO中新增的元素放在头部。
Map
包含键值对,键不能重复,每个键只能对应一个值。
这些接口中的哪些方法保证了这些数据结构能够拥有这些特性呢?Collection能够从这些数据结构中抽象出什么共同的特性而Map却不包含呢?接下来我们先分析Collection接口。
Collection接口的定义:
public interface Collection<E> extends Iterable<E>
是个泛型接口,而且是单值,所以无法实现Map这种键值对映射需要两个值的数据结构要求。
继承了迭代器,这样就能保证实现了Collection接口的类都能获取一个迭代器用于遍历元素。
所以Collection和Map最大的区别在于一个是单列集合,一个是双列集合。
但集合类并不是直接实现Collection接口而是继承抽象类AbstractCollection。AbstractCollection实现了Collection接口中的大多数方法,并且给出了集合所包含元素的最大数量为Integer.MAX_VALUE - 8
。再往下一层则是各个数据结构的抽象类,如AbstractList
, AbstractSet
, AbstractQueue
, AbstractMap
。这四个均继承AbstractCollection
。从这个层次开始,每个数据结构开始拥有自己的特性。以AbstractList
举例说明。定义如下:
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E>
AbstractList
从AbstractCollection
中重载了add(E)方法,clear()方法以及实现了iterator()方法。List
接口提供了对集合的增删改查,查找索引,列表迭代器,获取子列表等功能。
AbstractList
拥有两个内部类Itr
和ListItr
。这两个类分别代表迭代器和列表迭代器。AbstractList
中的iterator()方法返回的就是Itr的实例。Itr
比较短小,可以直接把源码贴上来
private class Itr implements Iterator<E> {
/**
* Index of element to be returned by subsequent call to next.
*/
int cursor = 0;
/**
* Index of element returned by most recent call to next or
* previous. Reset to -1 if this element is deleted by a call
* to remove.
*/
int lastRet = -1;
/**
* The modCount value that the iterator believes that the backing
* List should have. If this expectation is violated, the iterator
* has detected concurrent modification.
*/
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size();
}
public E next() {
checkForComodification(); //函数实现见最下面
try {
int i = cursor;
E next = get(i);
lastRet = i;
cursor = i + 1;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
AbstractList.this.remove(lastRet);
if (lastRet < cursor)
cursor--;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
由此可以看出iterator是不支持多线程操作的。
ListItr
定义:
private class ListItr extends Itr implements ListIterator<E>
ListItr
相对于Itr
来说只是多增加了一些针对列表的操作,不过多叙述。
再往下一层走就是具体的集合实现类了。比较常用的如ArrayList
,LinkedList
,HashMap
等均继承于对应的抽象类。下图展示了常用集合类的关系。
值得注意的是这里的集合类有些不是线程安全的,在多线程的环境下最好不要使用。JDK1.5后,有专门的concurrent包实现了集合的多线程操作。
再来说说Map。Map接口有一个内部接口Entry,它表示Map的一个key-value对。Map接口中还含有一个entrySet()方法用来返回Map.Entry的集合。你可以使用迭代器对获取该集合中的元素。
Set entries = map.entrySet();
if(entries != null){
Iterator<Map.Entry<String,String>> itr = entries.iterator();
while(itr.hasNext()){
Map.Entry<String,String> entry = iterator.next();
Object key = entry.getKey();
Object value = entry.getValue();
...;
}
}
Map中还有keySet()方法用于获取键的集合从而获取键对应的值(没有valueSet()方法,因为value可以重复)。相比较而言,使用entrySet()获取Map元素更为高效方便。也可以不使用迭代器。
for(Map.Entry<String,String> entry: map.entrySet()){
System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
}
Map中还有containsKey()方法用于判断是否存在某个键。由于Map集合允许值对象为null,当使用get()方法返回null时无法确定是不存在该键对象还是键对象返回的值是null。因此需要使用containsKey()。containsKey()方法具体实现如下:
public boolean containsKey(Object key) {
Iterator<Map.Entry<K,V>> i = entrySet().iterator();
if (key==null) {
while (i.hasNext()) {
Entry<K,V> e = i.next();
if (e.getKey()==null)
return true;
}
} else {
while (i.hasNext()) {
Entry<K,V> e = i.next();
if (key.equals(e.getKey()))
return true;
}
}
return false;
}
下图展示了常用Map类的关系
介绍完底层集合类的逻辑关系后,就可以深入到具体的实现类了。比如LinkedHashMap,从源码进行分析时层次就更清晰明了。