第9章 集合
集合框架¶
接口之间的关系¶

类之间的关系¶

具体集合¶
Java 集合类型 |
底层数据结构 |
|---|---|
ArrayList |
动态数组 |
Linkedlist |
双向链表 |
ArrayDeque |
双端队列(循环数组实现) |
HashSet |
哈希集合 |
TreeSet |
红黑树 |
EnumSet |
包含枚举类型值的集合 |
LinkedHashSet |
哈希集合(链表实现) |
PriorityQueeu |
优先队列 |
HashMap |
哈希表 |
TreeMap |
红黑树 |
EnumMap |
键只属于枚举类型值的表 |
LinkedHashMap |
哈希表(节点间链表连接,可以记住插入顺序) |
WeakhashMap |
哈希表(值没有外部强引用时会被垃圾回收) |
IdentityHashMap |
哈希表(使用 == 比较键) |
链表¶
通用迭代器:Iterator¶
- 只有实现了
Iterable接口的类才能for-each遍历,Iterable接口有iterator()方法返回迭代器类型,也可以用迭代器遍历。 LinkedList和ArrayList的Iterator位于元素之间的 间隙 (包括两端元素的左右)。hasNext()方法返回是否有下一个间隙。next()方法会返回当前迭代器的间隙右侧的元素的值,同时将迭代器移动到下一个间隙。
链表专用迭代器:ListIterator¶
ListIterator继承自Iterator,基于双向链表的特性,扩展Iterator没有实现的功能。hasPrevious方法返回是否有上一个间隙。previous()方法会返回当前迭代器的间隙左侧的元素的值,同时将迭代器移动到上一个间隙。set()方法可以修改next或previous访问的上一个元素。ListIterator支持查询当前迭代器的间隙左右两侧元素的索引。
动态数组¶
ArrayList 线程不安全,如果要线程安全的动态数组,用 Vector
哈希表¶
哈希表的 K 必须重写 hashCode() 方法和 equals() 方法。最好加上 @override 标记提醒自己。
红黑树¶
使用红黑树 TreeSet 和 TeeeMap 的 K 必须实现 Comparable<T> 接口,或者 Comparator 接口。
队列¶
队列 Queue<T> 和双端队列 Deque<T> 是接口,ArrayDeQue<T> 和 LinkedList<T> 都实现了这两个接口,其中 ArrayDeque<T> 是基于循环队列的实现,LinkedList<T> 是基于双向链表的实现。
队列的方法及其比较:
| 方法名 | 描述 | 异常行为 |
|---|---|---|
boolean add(E) |
将元素 E 入队 |
如果队列已满,会抛出异常 |
E remove() |
将队头元素出队并返回 | 如果队列为空,会抛出异常 |
E element() |
返回队头元素 | 如果队列为空,会抛出异常 |
boolean offer(E) |
将元素 E 入队 |
如果队列已满,会返回 false |
E poll() |
将队头元素出队并返回 | 如果队列已满,会返回 null |
E peek() |
返回队头元素 | 如果队列已满,会返回 null |
优先队列¶
和红黑树一样, 必须实现 Comparable<T> 接口,或者 Comparator 接口。
TreeMap 和 HashMap 补充¶
Java的红黑树迭代器不太灵活,不能像C++ STL那样二分出迭代器,只能二分出键值对。而且在迭代之前就要想清楚到底是只要值还是键值都要。- 两种
Map都不能用范围for,可以用forEach()方法,参数是函数式接口,例如map.forEach((k, v) -> System.out.println("key=" + k + ", value=" + v));或者得到视图再开始枚举。 - 不能像
C++中map[key]++,这种情况要用merge()方法。
算法¶
排序¶
Arrays.sort() 用于对数组排序,而如果要对 ArrayList 或者 LinkedList 排序,则需要调用对象的 sort() 方法。
sort() 方法是稳定排序。
前置代码:一个简单的 Pair 泛型类
class Pair<F extends Comparable<F>, S extends Comparable<S>> implements Comparable<Pair<F, S>> {
private F first;
private S second;
public Pair(F f, S s) {first = f; second = s;}
public F getFirst() {return first;}
public S getSecond() {return second;}
public void setFirst(F f) {first = f;}
public void setSecond(S s) {second = s;}
@Override
public int compareTo(Pair<F, S> o) {
int d = first.compareTo(o.first);
return d != 0 ? d : second.compareTo(o.second);
}
@Override
public String toString() {return "[" + first + ", " + second + "]";}
}
public class Main {
public static void main(String[] args) {
ArrayList<Pair<Integer, Integer>> a = new ArrayList<>();
... // 给 a 添加数据
}
}
下面开始介绍 a.sort() 方法,函数参数接受一个 Comparator 接口:
// 不要用 a.sort(null), 用 Comparator.naturalOrder() 可以提醒是否实现了 Comparable 接口
a.sort(Comparator.naturalOrder()); // 用 compareTo() 比较
a.sort(Comparator.reverseOrder()); // 逆序
a.sort(Comparator.comparing(Pair::getSecond)); // 用 getSecond() 比较
a.sort(Comparator.comparing(Pair::getSecond, Comparator.reverseOrder())); // 用 getSecond() 比较,逆序
a.sort(Comparator.comparing(Pair<Integer, Integer>::getSecond, Comparator.reverseOrder())
.thenComparing(Pair::getFirst)); // 先比较 getSecond() 降序,如果相同就比较 getFirst() 升序 注意这种情况要注明 Pair类型
a.sort(Comparator.comparing(p -> p.getFirst() + p.getSecond())); // 使用 lambda 表达式比较 getFirst() + getSecond()
洗牌¶
使用 Collections.shuffle() 对 ArrayList 类型随机打乱:
var numbers = new ArrayList<Integer>();
for (int i = 1; i <= 49; i++)
numbers.add(i);
Collections.shuffle(numbers);
二分查找¶
Collections.binarySearch() 查找一个元素是否出现在表中:
var numbers = new ArrayList<Integer>();
for (int i = 0; i <= 13; i++)
numbers.add(i);
for (int i = 15; i <= 49; i++)
numbers.add(i);
int pos = Collections.binarySearch(numbers, 14) // pos = -14
System.out.println(pos);
如果返回负数表示查询失败,-pos-1 表示这个元素应该插入的位置。
如果返回证书表示查询成功,pos 表示元素的位置。
Java 中没有类似 C++ 的 lower+bound() 方法。
最后更新:
2023-12-31
创建日期: 2023-12-14
创建日期: 2023-12-14