集合框架概述
目录
容器
在计算机科学中,容器是指实例为其他类的对象的集合的类、数据结构、或者抽象数据类型。换言之,它们以一种遵循特定访问规则的系统的方法来存储对象。容器的大小取决于其包含的对象(或元素)的数目。潜在的不同容器类型的实现可能在空间和时间复杂度上有所差别,这使得在给定应用场景中选择合适的某种实现具有灵活性。
Java Collections Framework(JCF)为 Java 开发者提供了通用的容器,始于 JDK 1.2。Java 容器里只能放对象,对于八种基本类型(short
int
long
float
double
boolean
byte
char
),需要将其包装后才能放到容器里。很多时候装箱和拆箱能够自动完成,这虽然会导致额外的性能和空间开销,但简化了设计和编程。
接口和实现类
为了规范容器的行为,JCF 定义了 14 种容器接口(collection interfaces),它们的关系如下图所示:
Map
接口没有继承自 Collection
接口,因为 Map
表示的是关联式容器而不是集合。但 Java 提供了从 Map
转换到 Collection
的方法,可以方便地将 Map
切换到集合视图。
实现
上述接口的通用实现见下表:
接口 | 实现类 | |
可变长数组 | 双向链表 | |
List | ArrayList | LinkedList |
Deque | ArrayDeque | LinkedList |
接口 | 实现类 | ||
哈希表 | 平衡二叉树 | 哈希表+链表 | |
Set | HashSet | TreeSet | LinkedHashSet |
Map | HashMap | TreeMap | LinkedHashMap |
泛型
所有容器的内部存放的都是 Object
对象,泛型机制可以简化编程,由编译器完成类型的强制转换,并且可以在编译阶段而不是运行阶段发现问题。JDK 1.4 及以前版本不支持泛型,类型转换需要显式完成。
List cars = new ArrayList();
cars.add("Audi");
cars.add("Benz");
cars.add("BMW");
for (int i = 0; i < cars.size(); i++) {
String car = (String) cars.get(i); // 显式类型转换
System.out.println(car);
}
JDK 1.5 开始支持泛型的写法,省去了类型的强制转换,并且如果添加了错误类型的元素会编译失败。
List<String> cars = new ArrayList<String>();
cars.add("Audi");
cars.add("Benz");
cars.add("BMW");
for (int i = 0; i < cars.size(); i++) {
String car = cars.get(i); // 隐式类型转换
System.out.println(car);
}
JDK 1.7 可以在声明中指定泛型,创建时无需再次指定,编译器会自动推断目标类型。
List<String> cars = new ArrayList<>();
迭代器
迭代器 Iterator
提供了遍历容器中元素的方法。只有容器本身清楚容器里元素的组织方式,因此迭代器只能通过容器本身得到。
List<String> cars = new ArrayList<>();
cars.add("Audi");
cars.add("Benz");
cars.add("BMW");
for (Iterator<String> iterator = cars.iterator(); iterator.hasNext();) {
String car = iterator.next();
System.out.println(car);
}
JDK 1.5 引入了增强 for 循环,简化了遍历容器或数组时的写法。
List<String> cars = new ArrayList<>();
cars.add("Audi");
cars.add("Benz");
cars.add("BMW");
for (String car : cars) {
System.out.println(car);
}
上述代码的输出结果均为:
Audi
Benz
BMW