Java Collection

Java 集合,也叫作容器,主要是由两大接口派生而来:一个是 Collection接口,主要用于存放单一元素;另一个是 Map 接口,主要用于存放键值对。对于Collection 接口,下面又有三个主要的子接口:List Set Queue

List

ArrayList

创建ArrayList

1
2
3
ArrayList<String> list = new ArrayList<>();            // 默认初始容量为10
ArrayList<Integer> listWithCapacity = new ArrayList<>(20); // 指定初始容量
ArrayList<String> listFromOther = new ArrayList<>(otherList); // 复制其他集合

添加元素

1
2
3
4
list.add("apple");                 // 添加到末尾
list.add(1, "banana"); // 插入指定位置
list.addAll(otherList); // 添加另一个集合中的所有元素
list.addAll(2, otherList); // 插入到指定位置

获取元素

1
2
3
4
5
6
String fruit = list.get(0);      // 根据索引获取元素
boolean hasApple = list.contains("apple"); // 是否包含某元素
int index = list.indexOf("banana"); // 第一次出现的索引
int lastIndex = list.lastIndexOf("banana"); // 最后一次出现的索引
int size = list.size(); // 获取元素个数
boolean isEmpty = list.isEmpty(); // 判断是否为空

修改元素

1
list.set(1, "orange");           // 替换指定位置的元素

删除元素

1
2
3
4
list.remove("apple");            // 删除第一次出现的元素(按值)
list.remove(0); // 删除指定索引位置的元素
list.removeAll(otherList); // 删除与另一个集合中相同的元素
list.clear(); // 清空所有元素

与数组之间的切换

1
2
3
List<String> sub = list.subList(1, 3);   // 获取子列表(包含头,不包含尾)
Object[] array = list.toArray(); // 转换为 Object 数组
String[] arr = list.toArray(new String[0]); // 转换为指定类型数组

遍历方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 普通 for 循环
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}

// 增强 for 循环
for (String s : list) {
System.out.println(s);
}

// Lambda 表达式
list.forEach(item -> System.out.println(item));

// 迭代器
Iterator<String> it = list.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}

排序与替换

1
2
3
4
5
Collections.sort(list);               // 升序排序
Collections.sort(list, Collections.reverseOrder()); // 降序排序
Collections.reverse(list); // 反转列表
Collections.shuffle(list); // 随机打乱顺序
Collections.fill(list, "x"); // 所有元素设为 x

线程安全处理

1
List<String> syncList = Collections.synchronizedList(new ArrayList<>());

LinkedList

创建 LinkedList

1
2
LinkedList<String> list = new LinkedList<>();
LinkedList<String> list2 = new LinkedList<>(otherCollection);

添加元素

1
2
3
4
5
6
7
list.add("apple");               // 添加到末尾(等同 addLast)
list.add(0, "banana"); // 插入指定位置
list.addFirst("first"); // 添加到开头
list.addLast("last"); // 添加到末尾
list.offer("offer"); // 队列尾部插入元素(返回 boolean)
list.offerFirst("offerFirst"); // 队列头部插入
list.offerLast("offerLast"); // 队列尾部插入

访问元素

1
2
3
4
5
6
String first = list.get(0);       // 获取指定位置元素
String first2 = list.getFirst(); // 获取第一个元素
String last = list.getLast(); // 获取最后一个元素
String peek = list.peek(); // 查看队列头(不删除)
String peekFirst = list.peekFirst(); // 查看头元素
String peekLast = list.peekLast(); // 查看尾元素

修改元素

1
list.set(1, "orange");           // 替换指定位置元素

删除元素

1
2
3
4
5
6
7
8
9
list.remove();                  // 删除第一个元素(抛异常)
list.remove(0); // 删除指定索引
list.remove("apple"); // 删除第一个匹配元素
list.removeFirst(); // 删除第一个元素
list.removeLast(); // 删除最后一个元素
list.poll(); // 删除队列头(返回null而不是抛异常)
list.pollFirst(); // 删除并返回第一个元素
list.pollLast(); // 删除并返回最后一个元素
list.clear(); // 清空所有元素

查找元素

1
2
3
boolean has = list.contains("apple");  // 是否包含
int index = list.indexOf("apple"); // 第一次出现位置
int lastIndex = list.lastIndexOf("apple"); // 最后一次出现位置

遍历方法

1
2
3
4
5
6
7
8
9
10
11
for (String s : list) {
System.out.println(s);
}

list.forEach(System.out::println);

Iterator<String> it = list.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}

与数组的转换

1
2
3
Object[] array = list.toArray();
String[] arr = list.toArray(new String[0]);
List<String> sub = list.subList(1, 3); // 包含索引1,不包含索引3

线程安全

1
List<String> syncList = Collections.synchronizedList(new LinkedList<>());

Queue/Deque

Queue 接口 抛出异常 返回特殊值
插入队尾 add(E e) offer(E e)
删除队首 remove() poll()
查询队首元素 element() peek()
Deque 接口 抛出异常 返回特殊值
插入队首 addFirst(E e) offerFirst(E e)
插入队尾 addLast(E e) offerLast(E e)
删除队首 removeFirst() pollFirst()
删除队尾 removeLast() pollLast()
查询队首元素 getFirst() peekFirst()
查询队尾元素 getLast() peekLast()
栈插入 push(E e)=addFirst(E e)
栈删除 pop()=removeFirst()

这两个接口的实现为ArrayDequeLinkedList:

  • ArrayDeque 是基于可变长的数组和双指针来实现,而 LinkedList 则通过链表来实现。
  • ArrayDeque 不支持存储 NULL 数据,但 LinkedList 支持。
  • ArrayDeque 是在 JDK1.6 才被引入的,而LinkedList 早在 JDK1.2 时就已经存在。
  • ArrayDeque 插入时可能存在扩容过程, 不过均摊后的插入操作依然为 O(1)。虽然 LinkedList 不需要扩容,但是每次插入数据时均需要申请新的堆空间,均摊性能相比更慢。

Java中推荐使用ArrayDeque作为队列和栈的实现类。

ArrayDeque is likely to be faster than Stack when used as a stack, and faster than LinkedList when used as a queue.
—— 来自 ArrayDeque 的官方 Javadoc

ArrayDeque

类型 方法 说明
添加 addFirst(E e) 从队头插入
添加 addLast(E e) / add(E e) 从队尾插入
移除 removeFirst() / pollFirst() 移除并返回队头元素
移除 removeLast() / pollLast() 移除并返回队尾元素
访问 getFirst() / peekFirst() 查看队头元素但不移除
访问 getLast() / peekLast() 查看队尾元素但不移除
栈操作 push(E e) 相当于 addFirst
栈操作 pop() 相当于 removeFirst
栈操作 peek() 相当于 peekFirst

作为栈(LIFO)

1
2
3
4
5
6
7
8
Deque<String> stack = new ArrayDeque<>();

stack.push("A");
stack.push("B");
stack.push("C");

System.out.println(stack.pop()); // C
System.out.println(stack.peek()); // B

作为队列(FIFO)

1
2
3
4
5
6
7
8
Deque<String> queue = new ArrayDeque<>();

queue.offer("A");
queue.offer("B");
queue.offer("C");

System.out.println(queue.poll()); // A
System.out.println(queue.peek()); // B

PriorityQueue

创建

1
2
3
4
PriorityQueue<>();                      // 默认初始容量11,使用自然顺序排序(元素必须实现Comparable)
PriorityQueue<>(int initialCapacity); // 指定初始容量,使用自然顺序
PriorityQueue<>(Comparator comparator); // 自定义排序规则
PriorityQueue<>(Collection c); // 根据已有集合创建(必须可以排序)
方法 描述
boolean add(E e) 添加元素,若超出容量自动扩容,抛出异常(推荐用 offer()
boolean offer(E e) 添加元素,返回 truefalse
E poll() 取出并移除队首元素(最小或最大),为空返回 null
E peek() 获取队首元素但不移除,队列为空返回 null
E remove() 移除并返回队首元素,若为空则抛出异常
boolean remove(Object o) 删除队列中指定元素,成功返回 true
boolean contains(Object o) 判断队列是否包含某个元素
int size() 获取队列中元素数量
void clear() 清空队列
boolean isEmpty() 判断队列是否为空
Object[] toArray() 转为数组(无排序保证)
<T> T[] toArray(T[] a) 转为指定类型的数组

ArrayBlockingQueue \ LinkedBlockingQueue

Map

HashMap / LinkedHashMap

基本方法 描述
V put(K key, V value) 添加键值对,若 key 已存在,则更新并返回旧值
V get(Object key) 获取指定 key 对应的 value,若不存在则返回 null
V remove(Object key) 移除指定 key 的键值对,并返回被删除的 value
boolean containsKey(Object key) 是否包含指定的 key
boolean containsValue(Object value) 是否包含指定的 value
int size() 返回映射中键值对的数量
boolean isEmpty() 是否为空
void clear() 清空所有键值对
遍历方法 描述
Set<K> keySet() 返回所有键组成的 Set 视图
Collection<V> values() 返回所有值组成的 Collection 视图
Set<Map.Entry<K, V>> entrySet() 返回所有键值对的集合(每个元素是一个 Map.Entry
方法 描述
V getOrDefault(Object key, V defaultValue) 获取指定 key 的 value,如果不存在返回默认值
V putIfAbsent(K key, V value) 如果 key 不存在才放入
boolean replace(K key, V oldValue, V newValue) 替换指定 key 的 value,要求当前值等于 oldValue
V replace(K key, V value) 替换 key 对应的 value,不判断旧值
void forEach(BiConsumer<K,V> action) 对每个键值对执行操作(lambda)
void compute(K key, BiFunction...) 根据 key 和旧值计算新值放入
void merge(K key, V value, BiFunction...) 如果 key 存在合并,不存在则添加
void computeIfAbsent(K key, Function...) key 不存在时,计算一个值并放入
void computeIfPresent(K key, BiFunction...) key 存在时,根据旧值计算新值放入