1. Java集合框架的基本结构
在Java编程语言中,集合框架(Collection Framework)是存储和操作对象组的最常用工具之一。要理解List、Set、Map与Collection的关系,首先需要明确Java集合框架的整体架构。
Java集合框架主要分为两大分支:单列集合(Collection)和双列集合(Map)。Collection接口是单列集合的根接口,它定义了存储一组对象的基本操作,而Map接口则定义了存储键值对的操作规范。
重要提示:Collection和Map是完全独立的两个接口,它们之间没有继承关系。这是很多初学者容易混淆的概念。
1.1 Collection接口的继承体系
Collection接口位于java.util包中,它有三个主要的子接口:
- List接口:有序集合,允许重复元素
- Set接口:无序集合,不允许重复元素
- Queue接口:队列,遵循特定的存取规则
java复制// Collection接口的基本定义
public interface Collection<E> extends Iterable<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);
boolean containsAll(Collection<?> c);
boolean addAll(Collection<? extends E> c);
boolean removeAll(Collection<?> c);
boolean retainAll(Collection<?> c);
void clear();
// Java 8新增的默认方法...
}
1.2 Map接口的独立地位
Map接口虽然也属于Java集合框架的一部分,但它并不继承自Collection接口。Map存储的是键值对(Key-Value pairs),这与Collection存储单个元素的方式有本质区别。
java复制// Map接口的基本定义
public interface Map<K,V> {
int size();
boolean isEmpty();
boolean containsKey(Object key);
boolean containsValue(Object value);
V get(Object key);
V put(K key, V value);
V remove(Object key);
void putAll(Map<? extends K, ? extends V> m);
void clear();
Set<K> keySet();
Collection<V> values();
Set<Map.Entry<K, V>> entrySet();
// 其他方法...
}
2. List、Set与Collection的关系
2.1 List接口的特性
List是Collection的直接子接口,它代表一个有序的集合(也称为序列)。List的主要特点包括:
- 元素有序:按照插入顺序保存元素
- 允许重复:可以包含相同的元素
- 允许null元素:可以包含一个或多个null元素
- 可通过索引访问:提供了基于整数索引的访问方法
常见的List实现类有:
- ArrayList:基于动态数组实现
- LinkedList:基于双向链表实现
- Vector:线程安全的动态数组实现
- Stack:后进先出(LIFO)的堆栈实现
java复制// List接口扩展了Collection接口
public interface List<E> extends Collection<E> {
// 新增了基于索引的操作
E get(int index);
E set(int index, E element);
void add(int index, E element);
E remove(int index);
int indexOf(Object o);
int lastIndexOf(Object o);
ListIterator<E> listIterator();
ListIterator<E> listIterator(int index);
List<E> subList(int fromIndex, int toIndex);
}
2.2 Set接口的特性
Set也是Collection的直接子接口,它代表一个不允许重复元素的集合。Set的主要特点包括:
- 元素唯一:不允许包含重复元素
- 无序性:不保证元素的顺序(某些实现如LinkedHashSet除外)
- 允许null元素:最多只能包含一个null元素
- 数学集合操作:支持并集、交集、差集等操作
常见的Set实现类有:
- HashSet:基于哈希表实现
- LinkedHashSet:基于哈希表和链表实现,保持插入顺序
- TreeSet:基于红黑树实现,元素有序
java复制// Set接口的定义,没有新增方法,但强化了语义约束
public interface Set<E> extends Collection<E> {
// 从Collection继承的方法,但语义上不允许重复元素
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);
boolean containsAll(Collection<?> c);
boolean addAll(Collection<? extends E> c);
boolean removeAll(Collection<?> c);
boolean retainAll(Collection<?> c);
void clear();
// Java 8新增的默认方法...
}
3. Map接口的独特地位
3.1 Map与Collection的本质区别
Map接口代表的是键值对的集合,这与Collection接口存储单个元素的方式有根本区别。Map的主要特点包括:
- 键值对存储:每个元素包含一个键和一个值
- 键唯一:不允许重复的键
- 值可重复:不同的键可以映射到相同的值
- 允许null:某些实现允许null键和null值
常见的Map实现类有:
- HashMap:基于哈希表实现
- LinkedHashMap:保持插入顺序
- TreeMap:基于红黑树实现,按键排序
- Hashtable:线程安全的哈希表实现
- ConcurrentHashMap:线程安全的高性能并发实现
java复制// Map.Entry接口定义键值对的内部表示
interface Entry<K,V> {
K getKey();
V getValue();
V setValue(V value);
// 其他方法...
}
3.2 Map与Collection的间接联系
虽然Map本身不继承Collection,但Map提供了三个集合视图(collection views):
- keySet():返回Map中所有键的Set视图
- values():返回Map中所有值的Collection视图
- entrySet():返回Map中所有键值对的Set视图
java复制Map<String, Integer> map = new HashMap<>();
map.put("A", 1);
map.put("B", 2);
Set<String> keys = map.keySet(); // ["A", "B"]
Collection<Integer> values = map.values(); // [1, 2]
Set<Map.Entry<String, Integer>> entries = map.entrySet(); // [A=1, B=2]
4. 常见误区与面试要点
4.1 常见理解误区
-
误区一:认为Map继承自Collection
- 事实:Map是完全独立的接口,与Collection同级
-
误区二:认为Set是有序集合
- 事实:基本Set实现(HashSet)不保证顺序,只有特殊实现(LinkedHashSet, TreeSet)才有序
-
误区三:认为List不允许null元素
- 事实:List允许null元素,但某些操作(如排序)可能抛出NullPointerException
-
误区四:认为Map的values()返回Set
- 事实:values()返回Collection,因为值允许重复
4.2 面试常见问题解析
-
ArrayList和LinkedList的区别?
- ArrayList基于动态数组,随机访问快(O(1)),插入删除慢(O(n))
- LinkedList基于双向链表,随机访问慢(O(n)),插入删除快(O(1))
-
HashMap的工作原理?
- 基于哈希表,使用链地址法解决冲突
- Java 8后,当链表长度超过阈值(8)会转为红黑树
- 扩容时重新哈希,容量变为原来的2倍
-
如何保证集合的线程安全?
- 使用Collections.synchronizedXXX方法包装
- 使用并发集合类(ConcurrentHashMap, CopyOnWriteArrayList)
- 使用Vector、Hashtable(不推荐,性能较差)
-
Comparable和Comparator的区别?
- Comparable是内比较器,定义在类内部
- Comparator是外比较器,可以定义多种比较方式
4.3 性能考量与最佳实践
-
集合初始化时指定容量
java复制// 不好的做法 List<String> list = new ArrayList<>(); // 好的做法 - 预估大小 List<String> list = new ArrayList<>(100); -
遍历Map的最佳方式
java复制Map<String, Integer> map = new HashMap<>(); // 方式一:遍历entrySet for (Map.Entry<String, Integer> entry : map.entrySet()) { String key = entry.getKey(); Integer value = entry.getValue(); } // 方式二:Java 8 forEach map.forEach((k, v) -> System.out.println(k + "=" + v)); -
避免在迭代过程中修改集合
java复制List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C")); // 错误做法 - 会抛出ConcurrentModificationException for (String s : list) { if (s.equals("B")) { list.remove(s); } } // 正确做法 - 使用迭代器 Iterator<String> it = list.iterator(); while (it.hasNext()) { if (it.next().equals("B")) { it.remove(); } } -
选择合适的集合实现
- 需要快速随机访问:ArrayList
- 频繁插入删除:LinkedList
- 需要去重:HashSet
- 需要保持插入顺序:LinkedHashSet
- 需要排序:TreeSet
- 键值对存储:HashMap
- 并发环境:ConcurrentHashMap
