异常处理
1. Collection类
Collection 接口是 Java 集合框架中的根接口之一,定义了所有集合类共有的基本操作和行为。它位于 java.util 包中,继承自 Iterable 接口,因此所有实现了 Collection 的类都可以使用增强 for 循环进行遍历。
主要特点和方法
基础操作:
add(E e):向集合中添加一个元素。remove(Object o):从集合中删除指定的元素。clear():清空集合中的所有元素。size():返回集合中元素的数量。isEmpty():判断集合是否为空。contains(Object o):判断集合中是否包含某个元素。
遍历功能:
- 继承自
Iterable的iterator()方法,可以获取一个迭代器遍历集合中的元素。 - 从 Java 8 开始,还可以通过
stream()方法使用 Stream API 进行流式操作。
- 继承自
批量操作:
addAll(Collection<? extends E> c):将指定集合中的所有元素添加到当前集合中。removeAll(Collection<?> c):删除当前集合中包含在指定集合中的所有元素。retainAll(Collection<?> c):保留当前集合中与指定集合共有的元素。
注意事项
抽象性质:
Collection接口不能被直接实例化,而是由其子接口(如List、Set、Queue等)来具体实现。无索引访问:
Collection接口没有定义基于索引的访问方法(例如get(int index)),如果需要索引访问,可以考虑使用List接口及其实现类(如ArrayList)。扩展性:
所有 Java 集合类都遵循Collection接口定义的契约,这使得集合类之间可以进行通用的编程,而不用关注具体的实现细节。
2. List
- get(int index) : 返回第index个元素
- set(int index, Object obj) : 把第index个元素设置成obj
有序性:
List中的元素按照插入顺序排列,开发者可以通过索引(从 0 开始)来访问、插入或删除元素。允许重复:
与Set不同,List允许集合中存在重复元素。扩展 Collection 接口:
除了Collection中的基本操作外,List还提供了与索引有关的操作,如get(int index)、set(int index, E element)、add(int index, E element)和remove(int index)。
主要方法
除了 Collection 提供的常用方法,List 接口还增加了以下与索引相关的操作:
添加和插入
boolean add(E e):在列表末尾追加元素。void add(int index, E element):在指定位置插入元素,其后的元素依次后移。
访问元素
E get(int index):返回指定位置的元素。E set(int index, E element):替换指定位置的元素,并返回原来的值。
删除元素
E remove(int index):删除指定位置的元素,并返回被删除的元素。boolean remove(Object o):删除第一次出现的指定对象(若存在)。
查找元素
int indexOf(Object o):返回元素第一次出现的位置,如果不存在返回 -1。int lastIndexOf(Object o):返回元素最后一次出现的位置。
子列表操作
List<E> subList(int fromIndex, int toIndex):返回列表中指定范围的视图,视图中的修改会反映在原列表上。
列表迭代器
ListIterator<E> listIterator():返回一个列表迭代器,可双向遍历列表,并在遍历过程中进行修改操作。
常见的实现类
Java 提供了多个 List 接口的实现,每种实现都有各自的特点和适用场景:
ArrayList
- 底层实现:动态数组
- 特点:
- 随机访问非常快(时间复杂度 O(1))
- 插入和删除(尤其是中间位置)可能较慢,因为需要移动数组元素
- 默认初始容量通常为 10,自动扩容时采用一定的增长策略
- 适用场景:频繁读取,较少修改(尤其是尾部添加)的场景
LinkedList
- 底层实现:双向链表
- 特点:
- 插入和删除操作快(特别是在列表两端或已知节点处进行操作)
- 随机访问较慢(需要遍历节点,时间复杂度 O(n))
- 除了作为
List,它还实现了Deque接口,适合当作队列或双端队列使用
- 适用场景:频繁在中间插入或删除元素的场景
Vector
- 底层实现:动态数组
- 特点:
- 与
ArrayList类似,但所有方法都是同步的,因此线程安全 - 由于同步开销,性能相对较低
- 作为较早期的集合类,现多推荐使用
ArrayList(如果线程安全需求,则可考虑Collections.synchronizedList或CopyOnWriteArrayList等)
- 与
- 适用场景:需要线程安全且对性能要求不太苛刻的场景
性能与使用注意
随机访问 vs 插入删除
- 如果你的应用需要频繁通过索引随机访问元素,建议使用
ArrayList。 - 如果需要在列表中间进行大量插入和删除操作,
LinkedList会更高效。
- 如果你的应用需要频繁通过索引随机访问元素,建议使用
线程安全问题
- 默认情况下,
ArrayList和LinkedList都不是线程安全的。如果需要线程安全的列表,可以使用Collections.synchronizedList(new ArrayList<>())或选择并发集合如CopyOnWriteArrayList(适合读多写少场景)。
- 默认情况下,
内存占用
ArrayList在扩容时可能会分配较大的连续内存块,而LinkedList每个节点都需要额外的指针空间。
3. Set
3.1 HashSet
用哈希表实现, 和cpp un_orderset一样,无序, $O(1)$ 插入查询。
1 | var se = new HashSet<E>(); |
3.2 TreeSet
使用红黑树实现, 和cpp普通set一样, $O(logn)$ 插入查询
存储的元素必须实现 Comparable 接口,或者在构造时提供 Comparator。默认是按第一个元素的大小从小到大排序额,
Comparable 接口里面的compareTo(Object O) 用于排列, 和Struct 里面的bool operator > 一样
- comparator() : 返回比较器,最普通的NULL
- first() 最小的元素
- last()
- headSet()
- tailSet()
1 | import java.util.Comparator; |
Map
Map没有继承Collection接口。
- put(key, value)
- containsKey(key)
- containsValue(Object value)
- get(Object key)
- keySet(): 返回key值形成的集合
- values() : 返回values形成的Collections集合
- entrySet() : 返回一个Map.Entry类型的Set, 里面有getKey和getValue方法(Map.Entry)
- merge(T Key, T Value, Operation) : Ope可以使用Lambda, 对已经有的Key进行合并操作
V getOrDefault(Object key, V defaultValue):获取键对应的值,如果不存在则返回默认值。void forEach(BiConsumer<? super K, ? super V> action):遍历 Map 中所有键值对。V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction):如果键不存在,则通过给定函数成一个值并插入。V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction):如果键存在,则对其值进行重新计算。V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction):合并键对应的值,可用于统计和聚合操作。
常见实现类
Java 提供了多种 Map 的实现,根据不同需求可以选择合适的实现类:
| 实现类 | 底层结构 | 排序/顺序 | 线程安全性 | 备注 |
|---|---|---|---|---|
| HashMap | 哈希表 | 无序 | 非线程安全 | 允许 null 键和 null 值 |
| LinkedHashMap | 哈希表 + 链表 | 维护插入顺序(或访问顺序) | 非线程安全 | 相比 HashMap,多了顺序维护,性能略低 |
| TreeMap | 红黑树 | 按键的自然顺序或 Comparator 排序 | 非线程安全 | 键必须实现 Comparable 接口或提供 Comparator |
| Hashtable | 哈希表 | 无序 | 线程安全(同步方法) | 不允许 null 键和 null 值(较老的实现,不推荐使用) |
| ConcurrentHashMap | 分段锁哈希表 | 无序 | 线程安全(高并发) | 高效支持并发操作,适用于多线程环境 |
Java Map 与 C++ STL Map 的对比
| 特性 | C++ STL Map | Java Map |
|---|---|---|
| 接口设计 | 容器类,通常直接作为类使用 | 接口(Map 接口)+具体实现类 |
| 排序/无序 | std::map(红黑树实现,自动排序),std::unordered_map(哈希表实现) |
TreeMap(红黑树实现,排序),HashMap(哈希表实现,无序) |
| 键允许 null | 不支持 null 键(C++ 中 null 不适用) | HashMap 允许一个 null 键;TreeMap 不允许 null 键 |
| 访问方式 | 使用 operator[] 和 at() 方法 |
使用 get() 和 put() 方法,不支持下标操作 |
| 迭代器 | 提供双向迭代器(对于有序 map) | 提供 keySet()/entrySet() 等视图,通常用 foreach 遍历 |
使用示例
下面是一个简单的示例,展示如何使用 Map 接口及其常用方法(以 HashMap 为例),并演示如何遍历 Map 的不同视图:
1 | import java.util.HashMap; |
说明:
- put():插入或更新键值对。
- get() / getOrDefault():获取指定键的值。
- containsKey():检查某个键是否存在。
- remove():删除指定键的映射。
- entrySet()、keySet()、values():提供对 Map 内部数据的视图,方便遍历。
6. 进阶:Map 的默认方法(Java 8+)
Java 8 引入了一系列默认方法,进一步简化了 Map 的操作。例如:
- computeIfAbsent()
如果指定的键不存在,则使用给定函数计算一个值并插入 Map:
1 | scoreMap.computeIfAbsent("David", key -> 80); // 如果 David 不存在,则赋值 80 |
- merge()
1
`scoreMap.merge("Alice", 5, (oldVal, newVal) -> oldVal + newVal); // Alice 的分数加 5`
这些默认方法使得对 Map 的操作更加灵活和简洁。
