Java常用集合类之一览众山小

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>

AbstractListAbstractCollection中重载了add(E)方法,clear()方法以及实现了iterator()方法。List接口提供了对集合的增删改查,查找索引,列表迭代器,获取子列表等功能。
AbstractList拥有两个内部类ItrListItr。这两个类分别代表迭代器和列表迭代器。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等均继承于对应的抽象类。下图展示了常用集合类的关系。
Java常用集合类之一览众山小_第1张图片
值得注意的是这里的集合类有些不是线程安全的,在多线程的环境下最好不要使用。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类的关系
Java常用集合类之一览众山小_第2张图片
介绍完底层集合类的逻辑关系后,就可以深入到具体的实现类了。比如LinkedHashMap,从源码进行分析时层次就更清晰明了。

你可能感兴趣的:(java基础)