Java Collection接口的继承体系及关系梳理
Java Collection接口的继承体系概述
在Java编程中,Collection
接口是集合框架的根接口之一,它定义了一组用于操作对象集合的方法。Java集合框架提供了一套丰富的接口和类,用于存储、检索、操作和管理数据集合。Collection
接口的继承体系展示了不同类型集合之间的关系,理解这个体系对于有效使用Java集合框架至关重要。
Java集合框架主要分为两大类:Collection
和Map
。Map
用于存储键值对,而Collection
则用于存储单个元素。Collection
接口继承体系中包含了多个子接口和实现类,这些接口和类根据其特性和用途又可以进一步分类。
主要子接口
List
接口:List
接口继承自Collection
接口,它表示一个有序的集合,允许重复元素。List
接口提供了基于索引的访问方法,使得可以精确地控制元素的插入位置、获取特定位置的元素以及对列表进行排序等操作。常见的List
实现类有ArrayList
、LinkedList
和Vector
。Set
接口:Set
接口同样继承自Collection
接口,它表示一个不包含重复元素的集合。集合中的元素无序且唯一,这意味着向Set
中添加重复元素时,Set
会忽略该操作。常见的Set
实现类有HashSet
、TreeSet
和LinkedHashSet
。Queue
接口:Queue
接口继承自Collection
接口,它用于存储等待处理的元素,通常遵循先进先出(FIFO)的原则。Queue
接口提供了一些特定的方法,如offer
用于向队列中添加元素,poll
用于从队列中移除并返回头元素,peek
用于返回头元素但不移除。常见的Queue
实现类有PriorityQueue
和LinkedList
(LinkedList
实现了Queue
接口)。
List
接口及其实现类
List
接口的特点与方法
List
接口是Collection
接口的子接口,它具有以下特点:
- 有序性:
List
中的元素按照插入顺序或指定的顺序存储,每个元素都有一个索引位置。 - 可重复性:
List
允许存储重复的元素。
List
接口定义了许多方法,除了继承自Collection
接口的方法外,还增加了一些基于索引的操作方法:
add(int index, E element)
:将指定元素插入到列表的指定位置。get(int index)
:返回列表中指定位置的元素。set(int index, E element)
:用指定元素替换列表中指定位置的元素。remove(int index)
:移除列表中指定位置的元素。indexOf(Object o)
:返回指定元素在列表中第一次出现的索引,如果列表不包含该元素,则返回 -1。lastIndexOf(Object o)
:返回指定元素在列表中最后一次出现的索引,如果列表不包含该元素,则返回 -1。
ArrayList
类
ArrayList
是List
接口的一个常用实现类,它基于数组实现。ArrayList
具有以下特点:
- 动态数组:
ArrayList
的容量会根据需要自动增长。当向ArrayList
中添加元素时,如果当前容量不足以容纳新元素,ArrayList
会自动创建一个更大的数组,并将原数组中的元素复制到新数组中。 - 随机访问效率高:由于
ArrayList
基于数组实现,通过索引访问元素的时间复杂度为O(1),因此随机访问效率很高。 - 插入和删除效率低:在
ArrayList
中插入和删除元素时,需要移动数组中的其他元素,时间复杂度为O(n),因此在列表中间插入和删除元素的效率较低。
以下是一个使用ArrayList
的代码示例:
import java.util.ArrayList;
import java.util.List;
public class ArrayListExample {
public static void main(String[] args) {
// 创建一个ArrayList对象
List<String> list = new ArrayList<>();
// 添加元素
list.add("Apple");
list.add("Banana");
list.add("Orange");
// 打印列表
System.out.println("List: " + list);
// 通过索引获取元素
String element = list.get(1);
System.out.println("Element at index 1: " + element);
// 在指定位置插入元素
list.add(1, "Grape");
System.out.println("List after insertion: " + list);
// 删除指定位置的元素
String removedElement = list.remove(2);
System.out.println("Removed element: " + removedElement);
System.out.println("List after removal: " + list);
}
}
LinkedList
类
LinkedList
也是List
接口的一个实现类,它基于双向链表实现。LinkedList
具有以下特点:
- 动态链表:
LinkedList
的每个节点包含前驱节点和后继节点的引用,因此可以在链表的任意位置高效地插入和删除元素。 - 随机访问效率低:由于
LinkedList
不是基于数组实现,通过索引访问元素时需要从头或从尾遍历链表,时间复杂度为O(n),因此随机访问效率较低。 - 插入和删除效率高:在
LinkedList
中插入和删除元素只需要修改节点的引用,时间复杂度为O(1),因此在列表中间插入和删除元素的效率很高。
以下是一个使用LinkedList
的代码示例:
import java.util.LinkedList;
import java.util.List;
public class LinkedListExample {
public static void main(String[] args) {
// 创建一个LinkedList对象
List<String> list = new LinkedList<>();
// 添加元素
list.add("Apple");
list.add("Banana");
list.add("Orange");
// 打印列表
System.out.println("List: " + list);
// 通过索引获取元素
String element = list.get(1);
System.out.println("Element at index 1: " + element);
// 在指定位置插入元素
list.add(1, "Grape");
System.out.println("List after insertion: " + list);
// 删除指定位置的元素
String removedElement = list.remove(2);
System.out.println("Removed element: " + removedElement);
System.out.println("List after removal: " + list);
}
}
Vector
类
Vector
是List
接口的一个古老实现类,它也基于数组实现。Vector
与ArrayList
类似,但Vector
是线程安全的,而ArrayList
是非线程安全的。由于线程安全会带来性能开销,因此在单线程环境下,ArrayList
通常比Vector
更高效。
Vector
的大部分方法与ArrayList
相同,但Vector
的一些方法,如addElement
、elementAt
等,是在Java早期版本中定义的,在现代Java编程中,推荐使用ArrayList
或Collections.synchronizedList
来创建线程安全的列表。
以下是一个使用Vector
的代码示例:
import java.util.Vector;
public class VectorExample {
public static void main(String[] args) {
// 创建一个Vector对象
Vector<String> vector = new Vector<>();
// 添加元素
vector.addElement("Apple");
vector.addElement("Banana");
vector.addElement("Orange");
// 打印向量
System.out.println("Vector: " + vector);
// 通过索引获取元素
String element = vector.elementAt(1);
System.out.println("Element at index 1: " + element);
// 在指定位置插入元素
vector.insertElementAt("Grape", 1);
System.out.println("Vector after insertion: " + vector);
// 删除指定位置的元素
vector.removeElementAt(2);
System.out.println("Vector after removal: " + vector);
}
}
Set
接口及其实现类
Set
接口的特点与方法
Set
接口继承自Collection
接口,它具有以下特点:
- 唯一性:
Set
中不允许存储重复的元素,当向Set
中添加重复元素时,Set
会忽略该操作。 - 无序性:
Set
中的元素通常是无序的,即元素的存储顺序与插入顺序无关。
Set
接口继承了Collection
接口的所有方法,没有额外定义新的方法。
HashSet
类
HashSet
是Set
接口的一个常用实现类,它基于哈希表实现。HashSet
具有以下特点:
- 快速查找:
HashSet
使用哈希表存储元素,因此添加、删除和查找元素的时间复杂度通常为O(1),效率很高。 - 无序性:
HashSet
中的元素是无序的,元素的存储顺序与插入顺序无关。
以下是一个使用HashSet
的代码示例:
import java.util.HashSet;
import java.util.Set;
public class HashSetExample {
public static void main(String[] args) {
// 创建一个HashSet对象
Set<String> set = new HashSet<>();
// 添加元素
set.add("Apple");
set.add("Banana");
set.add("Orange");
set.add("Apple"); // 重复元素,将被忽略
// 打印集合
System.out.println("Set: " + set);
// 检查集合是否包含某个元素
boolean contains = set.contains("Banana");
System.out.println("Set contains Banana: " + contains);
// 删除元素
set.remove("Orange");
System.out.println("Set after removal: " + set);
}
}
TreeSet
类
TreeSet
是Set
接口的另一个实现类,它基于红黑树实现。TreeSet
具有以下特点:
- 有序性:
TreeSet
中的元素是有序的,默认按照自然顺序(如数字从小到大、字符串按字典序)排序。也可以通过构造函数传入自定义的比较器来指定排序规则。 - 元素唯一性:与其他
Set
实现类一样,TreeSet
不允许存储重复元素。
以下是一个使用TreeSet
的代码示例:
import java.util.Set;
import java.util.TreeSet;
public class TreeSetExample {
public static void main(String[] args) {
// 创建一个TreeSet对象
Set<String> set = new TreeSet<>();
// 添加元素
set.add("Banana");
set.add("Apple");
set.add("Orange");
// 打印集合,元素按自然顺序排序
System.out.println("Set: " + set);
// 检查集合是否包含某个元素
boolean contains = set.contains("Banana");
System.out.println("Set contains Banana: " + contains);
// 删除元素
set.remove("Orange");
System.out.println("Set after removal: " + set);
}
}
LinkedHashSet
类
LinkedHashSet
是HashSet
的子类,它基于哈希表和双向链表实现。LinkedHashSet
具有以下特点:
- 有序性:
LinkedHashSet
中的元素按照插入顺序或访问顺序存储,保持了元素的插入顺序。 - 快速查找:由于
LinkedHashSet
基于哈希表实现,添加、删除和查找元素的时间复杂度通常为O(1),效率很高。
以下是一个使用LinkedHashSet
的代码示例:
import java.util.LinkedHashSet;
import java.util.Set;
public class LinkedHashSetExample {
public static void main(String[] args) {
// 创建一个LinkedHashSet对象
Set<String> set = new LinkedHashSet<>();
// 添加元素
set.add("Apple");
set.add("Banana");
set.add("Orange");
// 打印集合,元素按插入顺序排列
System.out.println("Set: " + set);
// 检查集合是否包含某个元素
boolean contains = set.contains("Banana");
System.out.println("Set contains Banana: " + contains);
// 删除元素
set.remove("Orange");
System.out.println("Set after removal: " + set);
}
}
Queue
接口及其实现类
Queue
接口的特点与方法
Queue
接口继承自Collection
接口,它用于存储等待处理的元素,通常遵循先进先出(FIFO)的原则。Queue
接口定义了一些特定的方法:
offer(E e)
:将指定元素添加到队列的尾部,如果队列已满,返回false
;否则返回true
。poll()
:移除并返回队列的头元素,如果队列为空,返回null
。peek()
:返回队列的头元素,但不移除,如果队列为空,返回null
。add(E e)
:将指定元素添加到队列的尾部,如果队列已满,抛出IllegalStateException
。remove()
:移除并返回队列的头元素,如果队列为空,抛出NoSuchElementException
。
PriorityQueue
类
PriorityQueue
是Queue
接口的一个实现类,它基于堆数据结构实现。PriorityQueue
中的元素按照自然顺序或自定义的比较器顺序进行排序,每次从队列中取出的元素是优先级最高的元素。
以下是一个使用PriorityQueue
的代码示例:
import java.util.PriorityQueue;
import java.util.Queue;
public class PriorityQueueExample {
public static void main(String[] args) {
// 创建一个PriorityQueue对象
Queue<Integer> queue = new PriorityQueue<>();
// 添加元素
queue.offer(3);
queue.offer(1);
queue.offer(2);
// 打印队列
System.out.println("Queue: " + queue);
// 取出并打印优先级最高的元素
Integer element = queue.poll();
System.out.println("Polled element: " + element);
System.out.println("Queue after poll: " + queue);
}
}
LinkedList
类作为Queue
的实现
前面提到LinkedList
不仅实现了List
接口,还实现了Queue
接口。因此,可以将LinkedList
当作Queue
来使用,利用其链表结构的特点实现高效的队列操作。
以下是一个将LinkedList
当作Queue
使用的代码示例:
import java.util.LinkedList;
import java.util.Queue;
public class LinkedListAsQueueExample {
public static void main(String[] args) {
// 创建一个LinkedList对象,并当作Queue使用
Queue<String> queue = new LinkedList<>();
// 添加元素
queue.offer("Apple");
queue.offer("Banana");
queue.offer("Orange");
// 打印队列
System.out.println("Queue: " + queue);
// 取出并打印队列头元素
String element = queue.poll();
System.out.println("Polled element: " + element);
System.out.println("Queue after poll: " + queue);
}
}
Collection
接口继承体系中的其他接口和类
Deque
接口及其实现类
Deque
(双端队列)接口继承自Queue
接口,它允许在队列的两端进行插入和删除操作。Deque
接口提供了更多的方法,如addFirst
、addLast
、removeFirst
、removeLast
等。
ArrayDeque
和LinkedList
是Deque
接口的两个常见实现类。ArrayDeque
基于数组实现,具有较高的性能;LinkedList
基于链表实现,在插入和删除操作上具有灵活性。
以下是一个使用ArrayDeque
的代码示例:
import java.util.ArrayDeque;
import java.util.Deque;
public class ArrayDequeExample {
public static void main(String[] args) {
// 创建一个ArrayDeque对象
Deque<String> deque = new ArrayDeque<>();
// 添加元素到队首和队尾
deque.addFirst("Apple");
deque.addLast("Banana");
deque.addFirst("Grape");
// 打印双端队列
System.out.println("Deque: " + deque);
// 移除并打印队首和队尾元素
String removedFirst = deque.removeFirst();
System.out.println("Removed first element: " + removedFirst);
String removedLast = deque.removeLast();
System.out.println("Removed last element: " + removedLast);
System.out.println("Deque after removal: " + deque);
}
}
BlockingQueue
接口及其实现类
BlockingQueue
接口继承自Queue
接口,它提供了线程安全的队列操作。BlockingQueue
的特点是当队列为空时,从队列中获取元素的操作会阻塞;当队列已满时,向队列中添加元素的操作会阻塞。
常见的BlockingQueue
实现类有ArrayBlockingQueue
、LinkedBlockingQueue
、PriorityBlockingQueue
等。这些实现类在多线程编程中常用于生产者 - 消费者模型。
以下是一个简单的生产者 - 消费者模型示例,使用ArrayBlockingQueue
:
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
class Producer implements Runnable {
private BlockingQueue<Integer> queue;
public Producer(BlockingQueue<Integer> queue) {
this.queue = queue;
}
@Override
public void run() {
try {
for (int i = 1; i <= 5; i++) {
System.out.println("Producing: " + i);
queue.put(i);
Thread.sleep(1000);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
class Consumer implements Runnable {
private BlockingQueue<Integer> queue;
public Consumer(BlockingQueue<Integer> queue) {
this.queue = queue;
}
@Override
public void run() {
try {
for (int i = 1; i <= 5; i++) {
Integer num = queue.take();
System.out.println("Consuming: " + num);
Thread.sleep(1500);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
public class BlockingQueueExample {
public static void main(String[] args) {
BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(3);
Thread producerThread = new Thread(new Producer(queue));
Thread consumerThread = new Thread(new Consumer(queue));
producerThread.start();
consumerThread.start();
try {
producerThread.join();
consumerThread.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
NavigableSet
接口及其实现类
NavigableSet
接口继承自SortedSet
接口,它提供了更丰富的导航方法,如lower
、floor
、ceiling
、higher
等,用于在有序集合中查找与给定元素相关的元素。
TreeSet
是NavigableSet
接口的主要实现类,通过TreeSet
可以方便地使用NavigableSet
接口定义的导航功能。
以下是一个使用TreeSet
作为NavigableSet
的代码示例:
import java.util.NavigableSet;
import java.util.TreeSet;
public class NavigableSetExample {
public static void main(String[] args) {
NavigableSet<Integer> set = new TreeSet<>();
set.add(1);
set.add(3);
set.add(5);
set.add(7);
// 使用导航方法
Integer lower = set.lower(4);
System.out.println("Lower than 4: " + lower);
Integer floor = set.floor(4);
System.out.println("Floor of 4: " + floor);
Integer ceiling = set.ceiling(4);
System.out.println("Ceiling of 4: " + ceiling);
Integer higher = set.higher(4);
System.out.println("Higher than 4: " + higher);
}
}
通过深入理解Collection
接口的继承体系及各个接口和类的特点与关系,开发者能够根据具体的需求选择最合适的集合类型,从而编写出高效、健壮的Java程序。在实际应用中,要综合考虑集合的性能、线程安全性、元素唯一性和有序性等因素,以充分发挥Java集合框架的强大功能。