一、简介
集合的定义和作用
Java集合是用于存储和操作一组对象的数据结构。它提供了一组接口和类,用于处理不同类型的集合数据,如列表、集、映射等。
Java集合的主要作用是:
存储对象:Java集合可以存储不同类型的对象,并提供了方便的方法来添加、删除和访问集合中的元素。
管理数据:集合提供了多种数据结构,如列表、集、映射等,可以根据不同的需求选择合适的数据结构来管理数据。比如,列表可以按照插入顺序存储数据,集可以保证元素的唯一性,映射可以通过键值对来存储和访问数据等。
提供算法和操作:Java集合提供了一系列算法和操作,如排序、查找、过滤等,可以方便地对集合中的元素进行处理和操作。
提高效率:集合类中的数据结构和算法经过优化,可以提高程序的执行效率。比如,使用HashSet来存储大量数据时,可以通过散列算法快速查找元素,而不需要遍历整个集合。
集合框架的基本概念
Java集合框架是Java编程语言提供的一组类和接口,用于存储、操作和处理数据集合。它提供了各种类型的数据结构,如列表、集合、映射等,以及用于操作和处理这些数据结构的算法和工具。Java集合框架的设计目标是提供高性能、可靠性和可扩展性的数据结构和算法。
Java集合框架的基本概念如下:
接口(Interface):Java集合框架中定义了许多接口,如List、Set、Map等。接口定义了一组操作集合的方法,是集合框架的核心部分。
类(Class):Java集合框架提供了实现接口的具体类,如ArrayList、HashSet、HashMap等。这些类实现了接口定义的方法,提供了具体的数据结构和算法。
列表(List):列表是有序的集合,可以包含重复的元素。Java集合框架提供了多个列表的实现类,如ArrayList、LinkedList等。
集合(Set):集合是无序的、不重复的元素的集合。Java集合框架提供了多个集合的实现类,如HashSet、TreeSet等。
映射(Map):映射是一种键值对的集合,每个键对应一个值。Java集合框架提供了多个映射的实现类,如HashMap、TreeMap等。
队列(Queue):队列是一种先进先出(FIFO)的数据结构。Java集合框架提供了多个队列的实现类,如LinkedList、PriorityQueue等。
迭代器(Iterator):迭代器用于遍历集合中的元素,提供了统一的访问集合元素的方式。通过迭代器,可以依次访问集合中的每个元素。
泛型(Generics):Java集合框架使用泛型来增加类型安全性和代码的可读性。通过泛型,可以指定集合中存储的元素类型。
算法(Algorithm):Java集合框架提供了许多算法和工具类,用于对集合进行排序、搜索、过滤等操作。这些算法和工具类可以方便地操作集合中的元素。
Java集合框架的使用非常广泛,它提供了丰富的数据结构和算法,可以满足各种不同的编程需求。
集合框架的接口和实现类
Java集合框架提供了一些核心接口和相关的实现类,用于存储和操作不同类型的集合数据。
Collection 接口:
- List 接口:有序、可重复的集合,可以通过索引访问元素。常见的实现类有 ArrayList、LinkedList、Vector。
- Set 接口:无序、不可重复的集合,不允许存储相同元素。常见的实现类有 HashSet、TreeSet、LinkedHashSet。
Map 接口:
- HashMap 类:无序的键值对集合,根据键来存储和访问。常用于快速查找和存储关联数据。
- TreeMap 类:有序的键值对集合,按照键的自然顺序或特定比较器定义的顺序来存储和访问。
- LinkedHashMap 类:有序的键值对集合,按照插入顺序或访问顺序来存储和访问。
Queue 接口:
- LinkedList 类:实现了队列的数据结构,支持先进先出(FIFO)的元素访问方式。
栈(Stack)接口:
- Stack 类:继承自 Vector 类,实现了后进先出(LIFO)的元素访问方式。
接口工具类:
- Collections 类:提供了一组静态方法,用于对集合对象进行常见操作,如排序、查找、反转等。
除了上述接口和实现类之外,Java集合框架还提供了其他接口和实现类,用于特定的集合需求。例如:
- Deque 接口:双端队列,可以在两端插入和删除元素。
- PriorityQueue 类:优先队列,按照元素的顺序进行访问,优先级高的元素先被访问。
- BitSet 类:位集合,用于存储和操作位信息。
集合框架的继承结构
Collection 接口:是所有集合的基本接口,定义了对集合元素的基本操作。它继承自 Iterable 接口,表示支持迭代访问。
List 接口:继承自 Collection 接口,表示有序、可重复的集合。List 接口提供了按索引访问和操作集合元素的方法。常见的实现类有 ArrayList、LinkedList、Vector。
Set 接口:继承自 Collection 接口,表示无序、不可重复的集合。Set 接口不允许存储相同的元素。常见的实现类有 HashSet、TreeSet、LinkedHashSet。
Queue 接口:继承自 Collection 接口,表示一种特殊的集合,用于实现队列(先进先出)的数据结构。Queue 接口提供了添加、删除和检查元素的方法。常见的实现类有 LinkedList、PriorityQueue。
Deque 接口:继承自 Queue 接口,表示双端队列,可以在两端插入和删除元素。Deque 接口提供了在队首、队尾进行插入和删除操作的方法。常见的实现类有 LinkedList、ArrayDeque。
Map 接口:表示键值对的集合,每个键都是唯一的。Map 接口定义了对键值对进行操作的方法。常见的实现类有 HashMap、TreeMap、LinkedHashMap。
除了上述基本接口之外,还有一些继承于上述接口的子接口和实现类,用于扩展集合框架的功能。
二、List集合
List集合的接口和实现类
List 是 Java 集合框架中的接口之一,它继承自 Collection 接口。List 表示有序、可重复的集合,每个元素都有一个对应的索引,可以通过索引访问和操作元素。
List 接口:
- 方法:List 接口继承自 Collection 接口,除了 Collection 接口的方法外,额外提供了一些特有的方法,如根据索引操作元素、获取子列表等。
- 实现类:List 接口的主要实现类包括 ArrayList、LinkedList 和 Vector。
ArrayList 类:
- 实现了 List 接口,底层使用数组来存储元素,支持动态扩容。
- 优点:随机访问元素快速,效率高;适用于频繁的随机访问和遍历操作。
- 缺点:插入和删除元素的效率相对较低,需要移动大量元素。
- 线程不安全:ArrayList 不是线程安全的,若需多线程操作,建议使用线程安全的实现类 Vector 或 Collections 工具类的 synchronizedList 方法进行包装。
LinkedList 类:
- 实现了 List 和 Deque 接口,底层使用双向链表来存储元素。
- 优点:插入和删除元素的效率高,不需要移动大量元素;支持高效的首尾操作。
- 缺点:随机访问元素效率相对较低。
- 在需要频繁插入和删除元素的场景中使用 LinkedList 效果更好。
Vector 类:
- 实现了 List 接口,底层也使用数组来存储元素,类似于 ArrayList。
- 优点:线程安全,可以在多线程环境下使用;支持动态扩容。
- 缺点:相比 ArrayList,性能稍差。
- 注意:Vector 是较早的集合实现类,在现代开发中,一般推荐使用 ArrayList 或 LinkedList。
ArrayList和LinkedList的区别和优缺点
ArrayList 和 LinkedList 是 List 接口的两个常见实现类,它们在底层数据结构和性能特点上有所不同。
ArrayList:
- 底层数据结构:ArrayList 使用数组来实现,内部维护一个 Object 类型的数组,默认初始容量为 10。
- 优点:
- 随机访问元素快速:由于是基于数组实现,通过索引可以直接访问元素,查找和读取效率高。
- 空间效率高:相对于链表结构,数组具有连续的内存空间,不需要额外的存储空间。
- 迭代操作效率高:迭代操作和遍历效率高,适用于频繁的随机访问和遍历操作。
- 缺点:
- 插入和删除元素的效率较低:插入和删除元素需要移动大量元素,特别是在数组中间或开头进行操作时。
- 动态扩容开销:当元素超过当前容量时,需要进行动态扩容,可能导致性能下降。
LinkedList:
- 底层数据结构:LinkedList 使用双向链表来实现,每个节点包含前后指针以及元素值。
- 优点:
- 插入和删除元素的效率高:由于是链表结构,插入和删除元素只需调整相邻节点的指针,效率较高。
- 支持高效的首尾操作:对头部和尾部元素的插入和删除操作效率极高。
- 缺点:
- 随机访问元素效率相对较低:要查找特定索引的元素需要从头或尾开始遍历链表,时间复杂度为 O(n)。
- 需要更多存储空间:相对于数组,链表需要额外的指针来维护节点之间的连接关系。
选择 ArrayList 还是 LinkedList 取决于具体的应用场景和操作需求:
- 如果需要频繁进行随机访问、读取和遍历操作,而插入和删除操作较少,可以选择 ArrayList,它具有更高的访问和读取性能。
- 如果需要频繁进行插入和删除操作,尤其是在链表的头部或尾部进行操作,则选择 LinkedList 更加合适,它具有更高的插入和删除性能。
- 如果涉及到多线程环境,建议使用线程安全的 Vector 或利用 Collections 工具类的 synchronizedList 方法包装 ArrayList/LinkedList。
List集合的常用操作和方法
添加元素操作:
boolean add(E element)
: 将指定元素添加到列表的末尾。void add(int index, E element)
: 在指定索引位置插入指定元素。boolean addAll(Collection c)
: 从列表中删除与指定集合中相同的所有元素。void clear()
: 清空列表中的所有元素。
获取元素操作:
E get(int index)
: 返回指定索引位置的元素。int indexOf(Object o)
: 返回指定元素第一次出现的索引。int lastIndexOf(Object o)
: 返回指定元素最后一次出现的索引。List subList(int fromIndex, int toIndex)
: 返回列表中指定范围的子列表。
修改元素操作:
E set(int index, E element)
: 将指定索引位置的元素替换为指定元素。
判断元素是否存在:
boolean contains(Object o)
: 判断列表是否包含指定元素。boolean containsAll(Collection c)
: 判断列表是否包含指定集合中的所有元素。
判断集合大小和空判断:
int size()
: 返回列表中的元素个数。boolean isEmpty()
: 判断列表是否为空。
遍历操作:
- 使用 for-each 循环遍历列表中的元素。
- 使用迭代器(Iterator)遍历列表中的元素。
List集合的遍历方式
使用 for 循环遍历:
for (int i = 0; i < list.size(); i++) {
E element = list.get(i);
// 对元素进行操作
}
通过索引可以逐个访问列表中的元素,并进行相应的操作。
使用增强型 for 循环遍历(foreach 循环):
for (E element : list) {
// 对元素进行操作
}
增强型 for 循环可以直接遍历列表中的元素,无需使用索引。
使用迭代器(Iterator)遍历:
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
E element = iterator.next();
// 对元素进行操作
}
通过调用 iterator()
方法获取迭代器对象,利用 hasNext()
方法判断是否还有下一个元素,通过 next()
方法获取下一个元素进行操作。
使用 Stream API 遍历(Java 8 及以上):
list.stream().forEach(element -> {
// 对元素进行操作
});
使用 Stream API 的 stream()
方法将列表转换为流,然后利用 forEach()
方法对流中的每个元素进行操作。
三、Set集合
Set集合的接口和实现类
Set 接口是 Java 集合框架的一部分,它扩展了 Collection 接口。Set 表示一个不允许包含重复元素的集合。
Set 接口的特点主要包括:
常见的 Set 实现类包括:
- HashSet:基于哈希表实现。HashSet 使用 HashMap 存储元素,并使用元素的哈希码进行索引,因此添加、删除和查找操作的时间复杂度为 O(1)。它是最常用的 Set 实现类,但不保证元素的顺序。
- LinkedHashSet:继承自 HashSet,基于哈希表和链表实现。它通过链表维护元素的插入顺序,因此可以有序地遍历元素。性能略低于 HashSet,但仍然比较高效。
- TreeSet:基于红黑树实现。TreeSet 中的元素会按照自然顺序(或者通过 Comparator 接口定义的顺序)进行排序。因此,TreeSet 可以提供一系列有序集合操作。
- EnumSet:专门用于存储枚举类型的 Set 集合。由于枚举类型的取值是有限且固定的,EnumSet 内部使用位向量实现,具有极高的效率。
需要注意的是,Set 接口本身不能直接实例化,必须使用具体的实现类来创建对象。示例代码如下:
Set set = new HashSet(); // 创建一个 HashSet 对象
根据需求和特定场景,选择适合的 Set 实现类。HashSet 是最常用的 Set 实现类,适用于大多数情况。如果需要保持插入顺序或按照自然顺序排序,可以选择 LinkedHashSet 或 TreeSet。当需要存储枚举类型的元素时,EnumSet 是最佳选择,因为它是高效的。
HashSet和TreeSet的区别和优缺点
内部实现机制不同:
- HashSet 使用哈希表(HashMap 实现)存储元素,根据元素的哈希码进行索引。因此,HashSet 具有快速的插入、删除和查找操作,时间复杂度为 O(1)。
- TreeSet 则是基于红黑树(TreeMap 实现)存储元素,它会按照元素的自然顺序(或者通过 Comparator 接口定义的顺序)进行排序。因此,TreeSet 提供了一些有序集合操作,如范围查找和排序等。
元素顺序:
- HashSet 中的元素没有特定的顺序,不保证元素的存取顺序和插入顺序一致。因为 HashSet 使用哈希表存储数据,元素在哈希表中的位置是根据元素的哈希码决定的,而不是插入的顺序。
- TreeSet 中的元素是有序的,根据元素的自然顺序进行排序。或者通过传入的 Comparator 接口来定义排序规则。
性能:
- HashSet 的插入、删除和查找操作的平均时间复杂度为 O(1)。但是,当哈希表发生冲突时,性能会有所下降,并且在极端情况下,最坏时间复杂度可能达到 O(n)。
- TreeSet 的插入、删除和查找操作的时间复杂度为 O(log n),其中 n 是 TreeSet 中元素的数量。由于红黑树的自平衡性质,它可以保持较为稳定的性能。
允许存储 null 元素的情况:
- HashSet 允许存储一个 null 元素(只能存储一个),因为哈希表中使用特殊的方式处理 null 值。
- TreeSet 不允许存储 null 元素,因为它需要对元素进行排序,而 null 值无法进行比较。
综上所述,HashSet 适用于需要快速插入、删除和查找的场景,并不关心元素的顺序。而 TreeSet 适用于需要有序集合操作的场景,例如范围查找和排序等,但由于红黑树的特性,其性能相对较低。同时,请注意 HashSet 和 TreeSet 都是线程不安全的,如果在多线程环境下使用,需要通过 Collections 工具类提供的 synchronizedSet 方法进行包装。
Set集合的常用操作和方法
添加元素:
boolean add(E element)
: 将指定元素添加到 Set 中。如果元素已经存在于 Set 中,则添加操作失败并返回 false。- 示例代码:
Set set = new HashSet(); set.add("apple"); set.add("banana");
移除元素:
boolean remove(Object o)
: 从 Set 中移除指定元素。如果元素存在于 Set 中,则移除操作成功并返回 true。- 示例代码:
set.remove("apple");
检查元素是否存在:
boolean contains(Object o)
: 判断 Set 中是否包含指定元素。如果元素存在于 Set 中,则返回 true。- 示例代码:
boolean contains = set.contains("banana");
获取元素数量:
int size()
: 返回 Set 中元素的数量。- 示例代码:
int size = set.size();
判断是否为空集:
boolean isEmpty()
: 判断 Set 是否为空集。如果 Set 不包含任何元素,则返回 true。- 示例代码:
boolean isEmpty = set.isEmpty();
清空集合:
void clear()
: 清空 Set 中的所有元素,使其变为空集。- 示例代码:
set.clear();
迭代器:
Iterator iterator()
: 返回一个迭代器,用于遍历 Set 中的元素。- 示例代码:
Iterator iterator = set.iterator(); while (iterator.hasNext()) { String element = iterator.next(); // 处理元素 }
需要注意的是,Set 集合没有提供按索引访问元素的方法,因为在 Set 中元素没有固定的顺序。
此外,Set 接口还继承了 Collection 接口中定义的一些方法,如addAll()
、removeAll()
、retainAll()
、containsAll()
等,用于集合之间的操作。
Set集合的遍历方式
迭代器遍历:
迭代器是一种用于遍历集合元素的通用方式,在遍历过程中允许修改集合。Set 集合的迭代器通过调用iterator()
方法获取,然后使用hasNext()
和next()
方法遍历元素。
示例代码:
Set set = new HashSet();
// 添加元素...
Iterator iterator = set.iterator();
while (iterator.hasNext()) {
String element = iterator.next();
// 处理元素
}
增强型 for 循环遍历:
增强型 for 循环是遍历集合元素的简化方式,适用于遍历整个集合的情况。它不需要显式地使用迭代器,只需指定一个变量来接收每个元素。
示例代码:
Set set = new HashSet();
// 添加元素...
for (String element : set) {
// 处理元素
}
forEach() 方法遍历(Java 8+):
Java 8 引入的 forEach() 方法提供了一种更简洁的遍历方式,可以使用 Lambda 表达式来处理每个元素。
示例代码:
Set set = new HashSet();
// 添加元素...
set.forEach(element -> {
// 处理元素
});
无论使用哪种遍历方式,都可以用于遍历 Set 集合中的所有元素。需要注意的是,在遍历过程中不要修改集合的内容,否则可能导致遍历失败或出现异常。另外,Set 集合中的元素没有固定的顺序,因此无法保证遍历的顺序与添加的顺序相同。
四、Map集合
Map集合的接口和实现类
Map 是 Java 集合框架中用于存储键值对的接口,它提供了一种将键映射到值的方式。在 Map 中,键是唯一的,而值可以重复。
常用的 Map 实现类:
HashMap:
HashMap 是最常用的 Map 实现类,它基于哈希表实现,允许使用 null 作为键和值。HashMap 不保证遍历顺序,性能较高。
TreeMap:
TreeMap 基于红黑树实现,按键的自然顺序或自定义比较器的顺序对键进行排序。TreeMap 不允许使用 null 作为键,但允许使用 null 作为值。
LinkedHashMap:
LinkedHashMap 继承自 HashMap,底层使用哈希表和双向链表实现。它保持了元素插入的顺序,并可以使用访问顺序进行迭代。
HashTable:
HashTable 是早期的 Map 实现类,基于哈希表实现。它不允许使用 null 作为键或值,线程安全但性能较差,通常不推荐使用。
HashMap和TreeMap的区别和优缺点
HashMap:
-
特点:
- 基于哈希表实现,通过键的哈希值进行快速查找和存储。
- 不保证遍历顺序,即元素的存储顺序与插入顺序无关。
- 允许使用 null 作为键和值。
- 非线程安全。
-
优点:
- 在大多数情况下,HashMap 的操作时间复杂度为 O(1),平均性能较高。
- 适用于不需要关心元素顺序的场景,例如快速查找、存储、删除元素。
- 内存占用相对较小。
-
缺点:
- 不保证元素的遍历顺序,所以无法按照插入顺序或者键的顺序进行迭代。
- HashMap 不是线程安全的,如果需要在多线程环境中使用,需要进行外部同步。
TreeMap:
-
特点:
- 基于红黑树实现,通过键的自然顺序或自定义比较器进行排序。
- 按键的有序状态维护元素,支持按键范围查询和遍历。
- 不允许使用 null 作为键,但允许使用 null 作为值。
- 非线程安全。
-
优点:
- TreeMap 保证元素的有序性,可以按照键的自然顺序或者自定义比较器的顺序进行迭代。
- 适用于需要按照键的顺序进行遍历、范围查询的场景。
- 提供了一些附加的方法,如 firstKey()、lastKey() 等。
-
缺点:
- 插入、删除和查找操作的时间复杂度为 O(logN),性能略低于 HashMap。
- 内存占用相对较大。
选择使用 HashMap 还是 TreeMap 取决于实际应用场景:
- 如果对于元素的插入、删除和查找操作要求性能较高,并且不关心元素的顺序,可以选择使用 HashMap。
- 如果需要按照键的顺序进行遍历、范围查询等操作,或者需要根据键的自然顺序进行排序,可以选择使用 TreeMap。
- 在多线程环境下,需要考虑线程安全性,可以使用 ConcurrentHashMap 来替代 HashMap 或 ConcurrentSkipListMap 来替代 TreeMap。
Map集合的常用操作和方法
添加和修改元素:
V put(K key, V value)
: 将指定的键值对添加到 Map 中,如果键已存在,则会覆盖原有的值并返回旧值。void putAll(Map list)
: 反转指定 List 中元素的顺序。
查找和替换操作:
binarySearch(List coll)
: 判断指定 Collection 是否为空。reverseOrder()
: 返回一个比较器,用于实现降序排序。shuffle(List list)
: 随机打乱指定 List 中元素的顺序。
Collections类的排序方法
Collections 类提供了多种排序方法来对集合进行排序。这些排序方法可以用于对 List、Set 和数组等集合进行排序。
sort(List list):
- 方法签名:
public static