Java编程思想拾遗(10)容器

207 阅读3分钟

通常程序总是根据运行时才知道的某些条件去创建对象,在此之前不会知道所需对象的数量,甚至不知道确切的类型,为解决这个普遍的编程问题,需要在任意时刻和任意位置创建任意数量的对象,所以就不能依靠创建命名的引用来持有每一个对象,因为你不知道实际上会需要多少这样的引用。

MyType aReference;

除了数组这种固定尺寸的结构,Java实用类库提供了一套相当完整的容器类来解决这个问题,其中基本的类型是List、Set、Queue和Map。

泛型与类型安全的容器

通过使用泛型,可以指定容器可以保存的类型,就可以在编译期防止将错误类型的对象放置到容器中,编译器可以阻止你将Orange放置到apples中,因此它变成了一个编译期错误,而不再是运行时错误。另外,在将元素从容器中取出时,类型转换也不再是必需的了。

当你指定了某个类型作为泛型参数时,你并不仅限于只能将该确切类型的对象放置到容器中,向上转型也可以像作用于其他类型一样作用于泛型,因此你可以将Apple的子类型添加到被指定为保存Apple对象的容器中。多态与泛型不冲突

迭代器

要使用容器,必须对容器的确切类型编程,如果原本是对着List编码的,但是后来发现如果能够把相同的代码应用于Set,将会显得非常方便,迭代器的概念可以用于达成次目的。

Java的Iterator只能单向移动,这个Iterator只能用来:

  • 使用方法iterator()要求容器返回一个Iterator接口,并准备好返回序列的第一个元素。
  • 使用next()获得序列中的下一个元素。
  • 使用hasNext()检查序列中是否还有元素。
  • 使用remove()将迭代器新近返回的元素删除。这就要求必须先调用next(),这个操作内部会重置cursor,所以可以继续迭代下去。

ListIterator是一个更加强大的Iterator的子类型,它只能用于各种List类的访问,尽管Iterator只能向前移动,但是ListIterator可以双向移动,它还可以产生相对于迭代器在列表中指向的当前位置的前一个和后一个元素的索引,并且可以使用set()方法替换它访问过的最后一个元素。

到目前为止,foreach语法主要用于数组,但是它也可以应用于任何Collection对象,Java引入了Iterable接口,该接口包含一个能够产生Iterator的iterator()方法,如果你创建了任何实现Iterable的类,都可以将它用于foreach语法中(数组是个特例不受这个限制)。

Java容器有一种保护机制,能够防止多个进程同时修改同一个容器的内容(其实同个线程内部也可能产生),如果在你迭代遍历某个容器的过程中,另一个进程介入其中,并且插入、删除或修改此容器内的某个对象,那么Java采用快速报错(fail-fast)机制,立刻抛出ConcurrentModificaitonException异常。

public class FailFast {
    public static void main(Stirng[] args) {
        Collection<String> c = new ArrayList<String>();
        Iterator<String> it = c.iterator();
        c.add(""An object);
        try {
            String s = it.next();
        } catch(ConcurrentModificaitonException e) {
            System.out.println(e);
        }
    }
}