接口
List接口
List
接口是Java集合框架中的一部分,它继承自 Collection
接口。List
提供了对元素进行有序集合操作的方法。
特性
- 有序:
List
接口中的元素是有序的,即元素按照它们被插入的顺序排列。 - 可重复:
List
允许存储重复的元素。 - 动态:
List
的大小可以根据需要动态变化。
常用方法
add(E e)
:向列表末尾添加一个元素。add(int index, E element)
:在指定位置插入一个元素。remove(int index)
:移除指定位置的元素,并返回被移除的元素。remove(Object o)
:移除列表中第一次出现的指定元素。get(int index)
:返回指定位置的元素。set(int index, E element)
:用指定元素替换列表中指定位置的元素。indexOf(Object o)
:返回第一次出现的指定元素的索引。lastIndexOf(Object o)
:返回最后一次出现的指定元素的索引。size()
:返回列表中的元素数量。isEmpty()
:如果列表为空,返回true
。
常用实现
ArrayList
- 基于动态数组实现。
- 支持快速随机访问。
- 插入和删除操作可能较慢,特别是当需要在列表中间插入或删除元素时。
LinkedList
- 基于双向链表实现。
- 适合进行频繁的插入和删除操作。
- 不支持快速随机访问。
Vector
(过时)
- 类似于
ArrayList
,但它是同步的。 - 通常不推荐使用,因为
ArrayList
提供了更好的性能。
Stack
(过时)
- 继承自
Vector
,实现了栈的功能。 - 遵循后进先出(LIFO)原则。
CopyOnWriteArrayList
- 线程安全的变体,适用于迭代操作远多于修改操作的场景。
Set接口
Set
接口是Java集合框架中的一个接口,它继承自 Collection
接口。Set
集合中的元素不允许重复,并且没有特定的顺序。以下是 Set
接口的一些关键特性和常用实现:
特性
- 不允许重复:
Set
接口保证不会包含重复的元素。 - 无序:尽管
Set
接口本身不保证元素的顺序,但是某些实现(如TreeSet
)可能会根据元素的自然顺序或自定义顺序对元素进行排序。
常用方法
add(E e)
:向集合添加一个元素。如果集合中已经存在该元素,则返回false
。remove(Object o)
:从集合中移除指定元素。contains(Object o)
:检查集合是否包含指定元素。size()
:返回集合中的元素数量。isEmpty()
:如果集合为空,返回true
。
常用实现
HashSet
- 基于哈希表实现。
- 性能通常优于其他
Set
实现,特别是在添加、删除和查找元素时。
LinkedHashSet
- 类似于
HashSet
,但它维护了元素的插入顺序。
TreeSet
- 基于红黑树实现。
- 可以按照自然顺序或自定义顺序对元素进行排序。
CopyOnWriteArraySet
- 线程安全的
Set
实现,适用于迭代操作远多于修改操作的场景。
示例用法
Set<String> set = new HashSet<>();
set.add("Apple");
set.add("Banana");
set.add("Cherry");
if (set.contains("Apple")) {
System.out.println("Set contains Apple");
}
set.remove("Banana");
System.out.println("Set size: " + set.size());
Queue接口
Queue
接口是Java集合框架中的一部分,它扩展了 Collection
接口,用于表示处理元素的队列。队列通常遵循先进先出(FIFO)的原则,但某些实现可能提供其他类型的队列行为,例如优先队列。
特性
- 先进先出:标准的队列实现遵循 FIFO 原则,即先添加到队列的元素将先被移除。
- 容量限制:某些队列实现可能具有容量限制,当队列满了之后,再尝试添加元素可能会阻塞或抛出异常。
- 阻塞行为:一些队列实现提供了阻塞操作,当队列为空时,从队列中获取元素的操作可能会阻塞,直到有元素可用。
常用方法
add(E e)
或offer(E e)
:向队列添加一个元素。offer
方法通常在无法添加元素时返回false
,而add
方法可能会抛出一个IllegalStateException
。remove(E e)
或poll()
:从队列移除并返回头部元素。poll
方法在队列为空时返回null
,而remove
方法可能会抛出一个NoSuchElementException
。element()
或peek()
:返回队列头部元素但不移除它。peek
方法在队列为空时返回null
,而element
方法可能会抛出一个NoSuchElementException
。size()
:返回队列中的元素数量。
常用实现
-
LinkedList
- 除了作为
List
的实现,LinkedList
也可以作为Queue
使用,因为它允许在两端进行快速的插入和删除操作。
- 除了作为
-
PriorityQueue
- 一个基于优先级堆的队列实现,元素按照自然顺序或自定义的比较器进行排序。
-
ArrayDeque
- 一个双端队列实现,允许在队列的两端进行快速的插入和删除操作。
-
阻塞队列(
java.util.concurrent
包下)- 提供了一系列阻塞队列实现,例如:
LinkedBlockingQueue
ArrayBlockingQueue
SynchronousQueue
DelayQueue
- 这些队列在进行插入或移除操作时可能会阻塞或等待。
- 提供了一系列阻塞队列实现,例如:
示例用法
Queue<String> queue = new LinkedList<>();
queue.add("Apple");
queue.add("Banana");
String fruit = queue.poll(); // 取出并移除队列头部的元素 "Apple"
System.out.println("Removed: " + fruit);
String frontFruit = queue.peek(); // 查看队列头部的元素但不移除 "Banana"
System.out.println("Front fruit: " + frontFruit);
非阻塞队列
非阻塞队列是一种队列实现,它在进行入队(enqueue)和出队(dequeue)操作时不会使线程阻塞。这种队列通常用于并发编程中,特别是在多线程环境中,它允许多个线程以非阻塞的方式访问队列。
特性
- 非阻塞:即使队列已满或为空,也不会使线程挂起或等待。
- 高吞吐量:由于没有线程阻塞,非阻塞队列通常能够提供较高的吞吐量。
- 低延迟:操作通常能够立即完成,减少了线程上下文切换的开销。
实现方式
非阻塞队列的实现通常依赖于以下技术:
- 原子操作:使用原子变量和原子操作来保证队列操作的原子性。
- 无锁编程:避免使用锁(synchronized blocks 或 locks),而是通过其他同步机制(如 CAS - Compare-And-Swap)来实现线程安全。
- 数据结构:使用特定的数据结构,如环形缓冲区(ring buffer),来实现高效的入队和出队操作。
Java中的非阻塞队列
在Java中,java.util.concurrent
包提供了一些非阻塞队列的实现,例如:
ConcurrentLinkedQueue
:基于链接节点的无界线程安全队列,用于在多个线程之间以 FIFO 顺序共享数据。LinkedTransferQueue
:类似于ConcurrentLinkedQueue
,但它提供了一些额外的特性,如支持在队列为空时,通过transfer
方法阻塞等待元素的插入。LinkedBlockingQueue
:虽然LinkedBlockingQueue
本身是一个阻塞队列,但它也可以配置为非阻塞模式,通过设置容量为Integer.MAX_VALUE
来实现。
示例用法
以下是使用 ConcurrentLinkedQueue
的一个简单示例:
import java.util.concurrent.ConcurrentLinkedQueue;
public class NonBlockingQueueExample {
private final ConcurrentLinkedQueue<Integer> queue = new ConcurrentLinkedQueue<>();
public void produce() {
for (int i = 0; i < 10; i++) {
queue.offer(i); // 非阻塞地添加元素
}
}
public void consume() {
Integer element;
while ((element = queue.poll()) != null) {
System.out.println("Consumed: " + element); // 非阻塞地移除并处理元素
}
}
public static void main(String[] args) {
NonBlockingQueueExample example = new NonBlockingQueueExample();
// 可以启动多个线程来并发地调用 produce 和 consume 方法
}
}
阻塞队列
阻塞队列(Blocking Queue)是一种特殊的队列,当队列进行入队(enqueue)或出队(dequeue)操作时,如果队列已满或为空,线程将会被阻塞,直到可以进行操作。这种队列在多线程环境中非常有用,因为它可以作为线程间通信的一种机制。
特性
- 线程安全:阻塞队列是线程安全的,可以由多个线程并发访问。
- 容量限制:阻塞队列可能有一个容量限制,超过这个限制后,入队操作将被阻塞,直到队列中有空间。
- 阻塞操作:当队列为空时,从队列中取出元素的操作将阻塞,直到队列中有元素可取;当队列已满时,入队操作将阻塞,直到队列中有空间。
常用方法
put(E e)
:向队列添加一个元素。如果队列已满,调用线程将被阻塞,直到队列中有空间。take()
:从队列中移除并返回头部元素。如果队列为空,调用线程将被阻塞,直到队列中有元素。offer(E e, long timeout, TimeUnit unit)
:尝试在指定的时间内向队列添加一个元素。如果超时前队列未满,则返回true
;否则返回false
。poll(long timeout, TimeUnit unit)
:尝试在指定的时间内从队列中移除并返回头部元素。如果超时前队列非空,则返回元素;否则返回null
。remainingCapacity()
:返回队列的剩余容量。
常用实现
Java的 java.util.concurrent
包提供了多种阻塞队列的实现,包括:
ArrayBlockingQueue
:基于数组的有界阻塞队列。LinkedBlockingQueue
:基于链表的可选容量阻塞队列。PriorityBlockingQueue
:基于优先级堆的无界阻塞队列。SynchronousQueue
:一个不存储元素的阻塞队列,每个插入操作必须等待一个相应的移除操作,反之亦然。DelayQueue
:基于优先级堆的无界阻塞队列,只持有实现了Delayed
接口的元素。LinkedTransferQueue
:类似于LinkedBlockingQueue
,但提供了更高效的入队和出队操作。
示例用法
以下是使用 ArrayBlockingQueue
的一个示例:
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class BlockingQueueExample {
private static final int QUEUE_SIZE = 10;
private final BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(QUEUE_SIZE);
public void produce() throws InterruptedException {
for (int i = 0; i < QUEUE_SIZE * 2; i++) {
queue.put(i); // 如果队列已满,这里会阻塞
System.out.println("Produced: " + i);
}
}
public void consume() throws InterruptedException {
for (int i = 0; i < QUEUE_SIZE * 2; i++) {
int number = queue.take(); // 如果队列为空,这里会阻塞
System.out.println("Consumed: " + number);
}
}
public static void main(String[] args) {
BlockingQueueExample example = new BlockingQueueExample();
Thread producer = new Thread(example::produce);
Thread consumer = new Thread(example::consume);
producer.start();
consumer.start();
}
}
Map接口
Map
接口是Java集合框架中用于存储键值对(key-value pairs)的一种数据结构。与 List
和 Set
集合不同,Map
允许我们通过键快速检索值。以下是 Map
接口的一些关键特性和常用实现:
特性
- 键值对:
Map
存储了键(key)和值(value)的映射关系,每个键映射到一个特定的值。 - 键唯一性:
Map
中的键是唯一的,不允许有重复的键。 - 无序:
Map
接口本身不保证映射的顺序,但某些实现(如LinkedHashMap
或TreeMap
)可以按照某种顺序来遍历键值对。
常用方法
put(K key, V value)
:将指定的值与此映射中的指定键关联。get(Object key)
:返回指定键所映射的值。remove(Object key)
:如果存在一个键的映射关系,则将其从映射中移除。keySet()
:返回映射中包含的键的 Set 视图。values()
:返回映射中包含的值的 Collection 视图。entrySet()
:返回映射中包含的键值映射关系的 Set 视图。size()
:返回映射中键值对的数量。isEmpty()
:如果映射不包含键值对,则返回 true。
常用实现
-
HashMap
- 基于哈希表的 Map 接口实现,它允许空键和空值。
- 性能高效,但顺序不保证。
-
TreeMap
- 基于红黑树的 Map 接口实现,可以按照键的自然顺序或自定义顺序对键值对进行排序。
-
LinkedHashMap
- 类似于
HashMap
,但它维护了插入顺序或者访问顺序,并且可以被配置为在达到最大容量时移除最老的条目。
- 类似于
-
Hashtable
- 与
HashMap
类似,但它是同步的,不允许空键和空值。
- 与
-
ConcurrentHashMap
- 线程安全的
HashMap
实现,用于高并发环境。
- 线程安全的
-
IdentityHashMap
- 一种特殊的
Map
实现,它使用==
而不是equals()
方法来比较键。
- 一种特殊的
示例用法
Map<String, Integer> map = new HashMap<>();
map.put("Apple", 1);
map.put("Banana", 2);
Integer value = map.get("Apple"); // 获取键 "Apple" 对应的值
System.out.println("Value: " + value);
map.remove("Banana"); // 移除键 "Banana" 的映射关系
System.out.println("Map size: " + map.size()); // 输出映射中键值对的数量