集合的接口与实现分离

与现代的数据结构类库的常见情况一样,Java 集合类库也将接口(interface)与实现(implementation)分离。

Collection 接口

在 Java 类库中,集合类的基本接口是 Collection 接口。除了 Map 的接口是 Map。

迭代器

集合类使用迭代器(Iterator)来访问集合的每个元素。
Iterator 接口包含 4 个方法:

  1. public interface Iterator<E> {
  2. E nexe();
  3. boolean hasNext();
  4. default void remove();
  5. default void forEachRemaining(Consumer<? super E> action);
  6. }

可以直接调用集合的 iterator() 来得到迭代器从而迭代集合:

  1. Collection<String> c = ...
  2. Iterator<String> iter = c.iterator();
  3. while(iter.hasNext()) {
  4. String element = iter.next();
  5. System.out.println(element);
  6. }

Java 封装了 for each 语句来让你更方便的迭代集合元素:

  1. for (String element : c) {
  2. System.out.println(element);
  3. }

其实 Java 编译器并不知道如何遍历集合原始。上述代码能够编译通过,只是因为编译器把 for each 循环通过 Iterator 改写为了循环:

  1. // for
  2. for (Iterator<String> iter = c.iterator(); iter.hasNext(); ) {
  3. String element = iter.next();
  4. System.out.println(element);
  5. }
  6. // while
  7. Iterator<String> iter = c.iterator();
  8. while(iter.hasNext()) {
  9. String element = iter.next();
  10. System.out.println(element);
  11. }

可以看到,在 Iterator 中还有一个 forEachRemaining() ,它可以和 lambda 表达式结合,遍历集合元素:

  1. list.iterator().forEachRemaining(element -> System.out.println(element));

在 Java 中,应该理解迭代器在两个元素之间。调用 next(),迭代器就越过下个元素,并返回越过那个元素的引用。这一点在使用 remove() 方法时应该深有体会。
remove() 会删除上次调用 next() 方法时返回的元素:

  1. Iterator<String> it = c.iterator();
  2. it.remove(); // 不能直接调用 remove(),因为此时的 it 还未接受 next 传来的对象
  3. Iterator<String> it = c.iterator();
  4. it.next();
  5. it.remove(); // OK
  6. Iterator<String> it = c.iterator();
  7. it.next();
  8. it.remove();
  9. it.remove(); // 连续调用将会抛出 IllegalStateException 异常,因为上一个 remove() 已经将 it 中 next 传来的对象移除
  10. Iterator<String> it = c.iterator();
  11. it.next();
  12. it.remove(); // OK
  13. it.next();
  14. it.remove(); // OK

集合框架中的接口

Java集合框架为不同类型的集合定义了大量接口:
chapter_IX-collection_interfece