除了以 Map 结尾的类之外,其他类都实现了 Collection 接口,而以 Map 结尾的类实现了 Map 接口。
Java 集合类库将接口与实现进行分离。以最常用的 List 为例,List 接口定义的统一格式,定义多个方法的约束而不用关心具体的实现。
public interface List<E> extends Collection<E> { int size(); boolean isEmpty(); boolean contains(Object o); Iterator<E> iterator(); Object[] toArray(); <T> T[] toArray(T[] a); boolean add(E e); boolean remove(Object o); ... }
研究 API 的时候会发现另外以 Abstract 开头的类,比如 AbstractList。这些是实现了相关接口的抽象类,为类库的实现者设计的。后面具体的实现可以更加轻松的扩展和重写抽象类的方法。
Collection 接口
集合类的基本接口是 Collection 接口。这个接口有定义了下面这些方法:
public interface Collection<E> extends Iterable<E> { // 返回集合中的元素个数。 int size(); // 当前集合存储的数量是否为 0。 boolean isEmpty(); // 集合中是否包含指定元素。 boolean contains(Object o); // 返回一个实现了 Iterator 接口(迭代器)的对象。可以使用这个迭代器对象依次访问集合中的元素。 Iterator<E> iterator(); // 将集合中的元素返回成一个数组。 Object[] toArray(); // 用于向集合中添加元素。如果添加元素确实改变了集合就返回 true,如果集合没有发生变化就返回 false。 boolean add(E e); // 从集合中删除一个元素。如果删除元素确实改变了集合就返回 true,如果集合没有发生变化就返回 false。 boolean remove(Object o); ... }
迭代器 Iterator
Iterator 接口包含四个方法:
public interface Iterator<E> { boolean hasNext(); E next(); default void remove(); default void forEachRemaining(Consumer<? super E> action); }
通过反复调用 next 方法,可以逐个访问集合中的每个元素。但是,如果到达了集合的末尾,next 方法将抛出一个 NoSuchElementException
。因此,需要在调用 next 之前调用 hasNext 方法。
public static void main(String[] args) { List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); Iterator<Integer> iterator = list.iterator(); while (iterator.hasNext()) { Integer i = iterator.next(); System.out.println(i); } }
编译器简单地将 for each 循环翻译为带有迭代器的循环。它可以与任何实现了 Iterable 接口的对象一起工作,这个接口只包含一个抽象方法:
public interface Iterable<T> { Iterator<T> iterator(); ... }
Collection 接口扩展了 Iterable 接口。因此,对于标准类库中的任何集合都可以使用 foreach 循环。
在 Java8 中,迭代器提供了另一种迭代方式,可以调用 forEachRemaining 方法并提供一个 lambda 表达式。
public static void main(String[] args) { List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); Iterator<Integer> iterator = list.iterator(); iterator.forEachRemaining(i -> System.out.println(i)); }
元素被访问的顺序取决于集合类型。如果对 ArrayList 进行迭代,迭代器将从索引 0 开始,每迭代一次,索引值加 1, 然而,如果访问 HashSet 中的元素,每个元素将会按照某种随机的次序出现,无法预知元素被访问的次序。
应该将 Java 迭代器认为是位于两个元素之间。当调用 next 时,迭代器就越过下一个元素,并返回刚刚越过的那个元素的引用。
**Iterator 接口的 remove 方法将会删除上次调用 next 方法时返回的元素。**在大多数情况下,在决定删除某个元素之前应该先看一下这个元素是很具有实际意义的。然而,如果想要删除指定位置上的元素,仍然需要越过这个元素。如果调 remove 之前没有调用 next 将是不合法的,抛出一个 IllegalStateException
异常。
泛型实用方法
由于 Collection 与 Iterator 都是泛型接口,可以编写操作任何集合类型的实用方法。在 Collection 接口中定义了很多非常有用的实用方法,所有实现类都必须提供这些方法的实现。这些方法的数量还是不少的,所以为了让实现者更容易得实现这个接口,Java 提供了 AbstractCollection 类,这个抽象类将一部分的接口方法已经实现,所以具体的集合类就可以继承这个抽象类进行下一步的实现。
集合框架中的接口
集合有两个基本接口:Collection 和 Map。
List 是一个有序集合。元素会增加到容器的指定位置。可以使用迭代器和整数索引进行访问。整数索引这种方法也称为随机访问,因为可以按照任意顺序(从头、从尾、中间、指定位置)访问元素。迭代器就只能按照顺序进行访问。
Set 接口等同于 Collection 接口,不过它不允许增加重复的元素。所以要适当的定义元素类的 equals 方法和 hashCode 方法。
Map 接口定义了存储键值对类型的数据的集合,添加数据时需要调用 put(key, value)
方法,一个 Map 中,key 是唯一的,所以重复添加会覆盖数据。
笔记大部分摘录自《Java核心技术卷I》,含有少数本人修改补充痕迹。