【Java】欸…?我学集合框架?真的假的?
Java集合框架
概述
Java集合框架主要由以下几个部分组成:
- 接口(Interfaces):定义了集合的基本操作,如添加、删除、遍历等。
- 实现(Implementations):提供了接口的具体实现,例如
ArrayList、HashMap等。 - 算法(Algorithms):如
Collections和Arrays类中定义的算法,用于操作集合元素。
为什么Java集合框架如此重要?
- 简化数据管理:集合框架提供了统一的方式来处理不同类型的数据集合。
- 提高性能:不同的集合类针对不同的使用场景进行了优化,可以提高程序的性能。
- 增强代码的可读性:使用集合框架可以使代码更加简洁和易于理解。
- 促进代码重用:集合框架的通用性使得编写的代码更容易被重用。
Collection接口
Collection接口概述
Collection 接口是Java集合框架中最基本的接口,它是所有单列集合的根接口。Collection 接口定义了适用于所有单列集合的操作,如添加、删除、遍历元素等。它提供了一系列通用的方法,使得对集合的操作变得统一和方便。
创建方式:
Collection<E> 对象名 = new 实现类对象<E>()
Collection接口的主要方法
以下是Collection接口中定义的一些核心方法:
boolean add(E e): 向集合中添加一个元素。boolean remove(Object o): 从集合中移除一个指定的元素。boolean contains(Object o): 检查集合是否包含指定的元素。int size(): 返回集合中的元素数量。boolean isEmpty(): 判断集合是否为空。boolean containsAll(Collection<?> c): 检查集合是否包含另一个集合的所有元素。boolean addAll(Collection<? extends E> c): 将指定集合的所有元素添加到当前集合中。boolean removeAll(Collection<?> c): 从当前集合中移除指定集合中的所有元素。boolean retainAll(Collection<?> c): 仅保留当前集合和指定集合共有的元素。void clear(): 清空集合中的所有元素。Iterator<E> iterator(): 返回一个迭代器,用于遍历集合中的元素。
迭代器
迭代器(Iterator)
迭代器是一种设计模式,用于顺序访问集合中的元素。在Java集合框架中,迭代器提供了一种统一的方法来遍历集合中的元素,而不需要了解集合的具体实现细节。Iterator接口定义在java.util包中,并且是java.lang.Iterable接口的一部分。
Iterator接口的主要方法
boolean hasNext(): 返回是否还有下一个元素可以迭代。E next(): 返回迭代的下一个元素。void remove(): 从集合中移除当前迭代的元素。
迭代器的使用
迭代器的使用通常如下:
复制Collection<E> collection = ...; // 某个集合实例
Iterator<E> iterator = collection.iterator();
while (iterator.hasNext()) {
E element = iterator.next();
// 处理元素
}
并发修改异常(ConcurrentModificationException)
在Java集合框架中,当一个集合在迭代过程中被修改(不是通过迭代器自身的remove方法),就会抛出ConcurrentModificationException异常。这个异常的目的是防止在迭代过程中集合结构被外部修改,这可能会导致不可预测的行为或违反迭代器的期望行为。
并发修改异常的常见场景
- 在迭代过程中直接调用集合的
add、remove等修改方法。 - 使用并发线程修改集合,而没有采取适当的同步措施。
避免并发修改异常的方法
- 使用迭代器的
remove方法来删除元素,该方法会告知迭代器集合已经被修改,从而避免异常。 - 使用
CopyOnWriteArrayList这样的并发集合,它们允许在迭代过程中进行修改,而不会引发异常。 - 在多线程环境下,使用适当的同步机制,如
synchronized块或ConcurrentHashMap等并发集合。

List接口
List接口
List接口是Collection接口的一个子接口,它是一个有序的集合,可以包含重复的元素。List接口提供了一些额外的方法,用于操作元素的顺序和插入点。
List接口的主要方法
void add(int index, E element): 在指定位置插入一个元素。E get(int index): 返回指定位置的元素。E set(int index, E element): 替换指定位置的元素。E remove(int index): 移除指定位置的元素并返回被移除的元素。int indexOf(Object o): 返回指定元素在列表中的第一次出现的位置。int lastIndexOf(Object o): 返回指定元素在列表中的最后一次出现的位置。ListIterator<E> listIterator(): 返回一个ListIterator(列表迭代器),允许对列表进行更复杂的操作。
ArrayList
ArrayList是基于数组实现的List接口的实现类。它允许对元素进行快速随机访问。
ArrayList的特点
- 动态数组:
ArrayList内部使用一个数组来存储元素,可以根据需要动态调整大小。 - 快速随机访问:通过索引访问元素非常快速。
- 不是线程安全的:
ArrayList不是线程安全的,多线程环境下需要外部同步。
ArrayList的适用场景
- 当需要快速访问列表中的元素时。
- 当列表的大小变化不是非常频繁时。
ArrayList的常用方法
add(E e): 在列表末尾添加一个元素。get(int index): 通过索引获取元素。remove(int index): 移除指定索引处的元素。
ArrayList的使用示例
List<String> arrayList = new ArrayList<>();
arrayList.add("Java");
arrayList.add("Python");
arrayList.add("C++");
String language = arrayList.get(1); // 获取索引为1的元素 "Python"
arrayList.remove(2); // 移除索引为2的元素 "C++"
LinkedList
LinkedList是基于链表实现的List接口的实现类,同时也是Queue接口的一个实现。
LinkedList的特点
- 双向链表:
LinkedList内部使用双向链表来存储元素。 - 插入和删除操作高效:在列表的头部、尾部或指定位置插入和删除元素非常高效。
- 随机访问慢:由于链表的特性,随机访问元素较慢。
LinkedList的适用场景
- 当需要频繁插入和删除元素时。
- 当需要实现栈、队列或双端队列时。
LinkedList的常用方法
add(E e): 在列表末尾添加一个元素。add(int index, E element): 在指定位置插入一个元素。remove(int index): 移除指定位置的元素。getFirst(): 获取列表的第一个元素。getLast(): 获取列表的最后一个元素。
LinkedList的使用示例
List<String> linkedList = new LinkedList<>();
linkedList.add("Java");
linkedList.add("Python");
linkedList.addFirst("C"); // 在列表头部添加元素
String firstLanguage = linkedList.getFirst(); // 获取第一个元素 "C"
linkedList.removeLast(); // 移除最后一个元素 "Python"
Vector
Vector是一个古老的List实现,与ArrayList类似,但它是同步的。
Vector的特点
- 同步的动态数组:
Vector内部使用一个数组来存储元素,并且是线程安全的。 - 性能较低:由于其线程安全性,
Vector的性能通常低于ArrayList。 - 遗留类:
Vector是Java早期版本的一部分,现在已经不推荐使用。
Vector的适用场景
- 在非常老的代码库中可能会遇到
Vector,但在新的代码中应该避免使用。
Vector的常用方法
与ArrayList类似,Vector提供了相同的方法,但由于其线程安全性,它还有一些额外的方法,如synchronized版本的迭代器。
Vector的使用示例
复制List<String> vector = new Vector<>();
vector.addElement("Java");
vector.addElement("Python");
vector.addElement("C++");
String language = vector.elementAt(1); // 获取索引为1的元素 "Python"
vector.removeElementAt(2); // 移除索引为2的元素 "C++"
Set接口
Set接口
Set接口是Java集合框架中Collection接口的一个子接口,它是一个不允许包含重复元素的集合。Set接口没有继承自List接口,因此它不保证元素的顺序,也不支持索引访问。
Set接口的主要方法
boolean add(E e): 添加一个元素,如果元素已存在,则返回false。boolean remove(Object o): 移除指定的元素。boolean contains(Object o): 检查集合是否包含指定的元素。Iterator<E> iterator(): 返回一个迭代器,用于遍历集合中的元素。
HashSet
HashSet是基于哈希表的Set实现,它不保证元素的顺序,并且允许空(null)元素。
HashSet的特点
- 基于哈希表:
HashSet的实现依赖于HashMap,因此它提供了快速的查找速度。 - 无序:元素没有特定的顺序。
- 允许单个null元素:
HashSet可以包含一个null元素。
HashSet的适用场景
- 当需要存储不重复的元素集合时。
- 当元素的顺序不重要时。
HashSet的常用方法
add(E e): 添加元素。remove(Object o): 移除元素。contains(Object o): 检查集合是否包含元素。
HashSet的使用示例
Set<String> hashSet = new HashSet<>();
hashSet.add("Apple");
hashSet.add("Banana");
hashSet.add("Cherry");
boolean containsBanana = hashSet.contains("Banana"); // 返回true
boolean removed = hashSet.remove("Apple"); // 返回true
TreeSet
TreeSet是基于红黑树的Set实现,它可以确保元素处于排序状态。
TreeSet的特点
- 有序:所有的元素都会按照自然顺序或构造时提供的比较器进行排序。
- 不允许null元素:与
HashSet不同,TreeSet不允许包含null元素。
TreeSet的适用场景
- 当需要有序的元素集合时。
- 当需要维护元素的排序状态时。
TreeSet的常用方法
add(E e): 添加元素到排序后的集合中。first(): 返回第一个(最小)元素。last(): 返回最后一个(最大)元素。
TreeSet的使用示例
Set<String> treeSet = new TreeSet<>();
treeSet.add("Apple");
treeSet.add("Banana");
treeSet.add("Cherry");
String firstFruit = treeSet.first(); // 返回"Apple"
String lastFruit = treeSet.last(); // 返回"Cherry"
LinkedHashSet
LinkedHashSet类似于HashSet,但它维护了元素的插入顺序。
LinkedHashSet的特点
- 有序:元素按照插入顺序进行排序。
- 性能:通常比
HashSet慢,因为需要维护插入顺序。
LinkedHashSet的适用场景
- 当需要存储不重复的元素集合,并且需要保持元素的插入顺序时。
LinkedHashSet的常用方法
与HashSet相同。
LinkedHashSet的使用示例
复制Set<String> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add("Apple");
linkedHashSet.add("Banana");
linkedHashSet.add("Cherry");
// 遍历将按照添加的顺序:Apple, Banana, Cherry
for (String fruit : linkedHashSet) {
System.out.println(fruit);
}
Map接口

Map接口
Map接口是Java集合框架的一部分,它存储的是键值对(key-value pairs),其中每个键(key)映射到一个值(value)。Map接口不保证元素的顺序,并且不允许键重复。
Map接口的主要方法
V put(K key, V value): 将指定的值与此映射中的指定键关联。V get(Object key): 返回指定键所映射的值。V remove(Object key): 移除指定键的映射关系并返回其值。int size(): 返回映射中键值对的数量。boolean isEmpty(): 判断映射是否为空。Set<K> keySet(): 返回映射中包含的键的Set视图。Collection<V> values(): 返回映射中包含的值的Collection视图。Set<Map.Entry<K, V>> entrySet(): 返回映射中包含的键值对的Set视图。
HashMap
HashMap是基于哈希表的Map实现,它允许空键和空值。
HashMap的特点
- 基于哈希表:
HashMap使用键对象的hashCode()值来存储和检索键值对。 - 无序:
HashMap不保证映射的顺序。 - 允许空键和空值:可以有一个或多个空键和空值。
HashMap的适用场景
- 当需要快速查找键值对时。
- 当元素的顺序不重要时。
HashMap的常用方法
put(K key, V value): 添加或更新键值对。get(Object key): 根据键获取值。remove(Object key): 根据键移除键值对。keySet(): 获取所有键的集合。values(): 获取所有值的集合。entrySet(): 获取所有键值对的集合。
HashMap的使用示例
Map<String, Integer> hashMap = new HashMap<>();
hashMap.put("Apple", 1);
hashMap.put("Banana", 2);
Integer appleCount = hashMap.get("Apple"); // 获取键"Apple"的值,返回1
hashMap.remove("Banana"); // 移除键"Banana"的键值对
TreeMap
TreeMap是基于红黑树的Map实现,它可以确保键的排序。
TreeMap的特点
- 有序:所有的键都会根据其自然顺序或构造时提供的比较器进行排序。
- 不允许空键:
TreeMap不允许空键,但可以有多个空值。
TreeMap的适用场景
- 当需要有序的键值对映射时。
- 当需要维护键的排序状态时。
TreeMap的常用方法
put(K key, V value): 添加或更新键值对。firstKey(): 返回第一个(最小)键。lastKey(): 返回最后一个(最大)键。descendingKeySet(): 返回按降序排列的键集合。
TreeMap的使用示例
Map<String, Integer> treeMap = new TreeMap<>();
treeMap.put("Apple", 1);
treeMap.put("Banana", 2);
String firstFruit = treeMap.firstKey(); // 获取第一个键,返回"Apple"
String lastFruit = treeMap.lastKey(); // 获取最后一个键,返回"Banana"
LinkedHashMap
LinkedHashMap类似于HashMap,但它维护了插入顺序或者访问顺序。
LinkedHashMap的特点
- 有序:元素按照插入顺序或访问顺序进行排序。
- 允许空键和空值:可以有一个或多个空键和空值。
LinkedHashMap的适用场景
- 当需要保持键值对的插入顺序或访问顺序时。
LinkedHashMap的常用方法
与HashMap相同。
LinkedHashMap的使用示例
Map<String, Integer> linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put("Apple", 1);
linkedHashMap.put("Banana", 2);
// 遍历将按照添加的顺序:Apple, Banana
for (Map.Entry<String, Integer> entry : linkedHashMap.entrySet()) {
System.out.println(entry.getKey() + " => " + entry.getValue());
}
结语
集合框架接口之间继承实现关系复杂,实现类繁多,要想运用自如还需多加努力,尽量对每一个常见的实现类都作充分了解,此后笔者还会继续学习泛型(真假泛型、泛型擦除)相关的知识。
参考文献:
Java集合框架最全详解(看这篇就够了)_java 集合框架-CSDN博客
Java集合框架最全详解(超详细)保姆级-CSDN博客
Java 备忘清单 & java cheatsheet & Quick Reference



















