CPT204 week 5-8

Week 5

Lists, Stacks, Queues, and Priority Queues

1) 为什么需要数据结构( Data Structure

    • 文档开头用"变量到处都是"引出:当数据很多时,需要更好的组织方式( store data)
    • 数据结构( Data Structure
      • 以特定方式组织数据的集合(collection of data organized in a specific fashion)
      • 不仅存数据(stores data),还提供访问与操作(operations for accessing/manipulating)
      • 高性能软件(high-performance software)需要选择合适的数据结构(best data structures)

2) 容器对象( Container Object )与面向对象思维( OOP thinking

    • 文档把数据结构比作"口袋/容器(container)"
    • 容器对象(Container Object)在 OOP 下有两层:
      • 类( Class / design :定义结构(structure)、字段(fields)、方法(methods:search/insert/delete...)
      • 实例( Instance / object :真正创建出来装具体数据的容器(actual container holding specific data)

3) Java Collections Framework 总览( Grand Hierarchy

    • Java Collections Framework 的"大分支":
      • Collections:存一组元素(stores a group of elements)
      • Maps :存 key/value(stores key/value pairs)
        并说明本周聚焦 Collections
    • Collection types(集合类型)列出 4 类:
      • Sets:无重复(no duplicates)
      • Lists:有序(ordered)
      • Queues:先进先出(FIFO)
      • PriorityQueues:按优先级(priority)

4) 接口与实现( Interfaces vs. Implementation

    • 文档用"蓝图/工具/成品"的比喻解释三层:
      • 接口( Interface / blueprint :定义共同操作(common operations)。
      • 抽象类( Abstract class / helper :提供部分实现(partial implementation)。
      • 具体类( Concrete class :可直接使用的数据结构实现(ready-to-use)。
    • 图里以 Collection<E> 为核心接口,下面分出:
      • Set → HashSet
      • List → ArrayList, LinkedList
      • Queue → PriorityQueue

5) Collection 接口:通用控制面板( Universal Controls

文档把 Collection 的方法看成"万能遥控器(universal remote)",适用于 Set/List/Queue。

分类可记成三组=:

5.1 修改类( Modification

    • add(e):添加元素(add element)
    • remove(o):删除指定元素(remove element)
    • clear():清空(clear all)

5.2 查询类( Query

    • size():元素数量(count elements)
    • isEmpty():是否为空(empty check)
    • contains(o):是否包含(membership check)

5.3 批量操作( Bulk Operations

    • addAll(c):把集合 c 全部加入(union-like add)
    • removeAll(c):移除所有出现在 c 的元素(difference-like remove)
    • retainAll(c):只保留共同元素(intersection-like keep common)

5.4 迭代器( Iterator

    • iterator():获得迭代器对象(get iterator)

6) 基础操作示例( Basic operations

6.1 添加( Adding data

    • 示例代码用 ArrayList<String> 依次 add("New York")、add("Atlanta")、add("Dallas")、add("Madison")。
    • 并说明:add(e) 返回 true 表示集合发生改变(collection changed)

6.2 查询与删除( Query operations

    • contains("Dallas") → 判断是否存在
    • remove("Dallas") → 删除指定元素
    • size() → 数量
    • isEmpty() → 是否空
    • 并用表格解释这些方法的含义与返回值(returns true / integer count)

6.3 批量操作 = 集合论( Bulk operations = Set theory

文档用 C1/C2 图示三种批量方法:

    • C1.addAll(C2):把 C2 的元素加到 C1(并集 union 的感觉)
    • C1.removeAll(C2):从 C1 删除所有出现在 C2 的(差集 difference 的感觉)
    • C1.retainAll(C2):C1 只保留共同元素(交集 intersection 的感觉)

7) 便利类( Convenience classes ): AbstractCollection

    • 如果要从零实现接口的全部方法(implement every interface method from scratch)会很繁琐(tedious)
    • 解决:AbstractCollection已经替你实现了大部分方法(implements most methods)。
    • 你只需实现少数核心:add、size、iterator

8) 不支持的操作( UnsupportedOperationException

    • 文档指出:不是所有集合都支持所有操作(not all collections support all operations),例如只读列表(read-only lists)
    • 常见处理方式:在不支持的方法里抛出 UnsupportedOperationException(Not supported)。
    • 这样保持接口一致(interface consistent),同时又允许不同实现的灵活性(flexibility)

9) 克隆与序列化( Cloning and Serialization

    • Cloneable:很多实现(ArrayList, LinkedList)可被克隆(can be cloned)。
    • Serializable:很多集合可以保存到文件/流(saved to files/streams)。
    • ⚠️ 警告:java.util.PriorityQueue 不实现 Cloneable(does NOT implement Cloneable)

10) 迭代与隐藏实现细节( Iterator Pattern

10.1 为什么需要迭代器( iterator

    • 文档对比 Array vs Collection
      • 数组(array)你知道怎么按下标访问
      • 集合(collection)内部结构可能是链表/网格等(unknown structure),但你仍想逐个查看元素而不暴露实现细节(without exposing implementation details)

10.2 三种遍历方式( Three ways to traverse

3 种遍历(traverse):

    • Iterator (手动 manual search :while(it.hasNext() ){ it.next();}
    • foreach 循环( foreach loop / quick scan :for (Element e : collection){ ...}
    • forEach 方法( forEach method / automated action :collection.forEach(e -> ...)
      并提示:如果遍历时要删除元素(remove while walking),用 iterator 更合适

10.3 Iterable / Collection / iterator()

Collection 继承/依赖 Iterable,并通过 iterator() 提供统一遍历方式(iterator pattern)

10.4 迭代器接口( Iterator interface ):统一遍历工具( uniform traversal

Iterator 的三个核心方法(core methods):

    • hasNext():检查是否还有下一个元素(Check: Is there more?)
      • 返回 boolean(returns boolean)
    • next():获取当前元素(Action: Give me the item!)
      • 返回元素(returns element)
    • remove():删除"上一次看到的元素"(Edit: Delete the last one I saw)
      • 返回 void(returns void)

Iterator 接口(Iterator interface)提供一种统一方式(uniform way)来遍历(traverse)各种集合(collections)

10.5 手动遍历( Manual operation = while + iterator

    • 创建集合(Collection)并加入数据:
      • Collection<String> collection = new ArrayList<>();
      • collection.add("New York");
      • collection.add("Atlanta");
    • 获取迭代器(Obtaining the gadget):
      • Iterator<String> iterator = collection.iterator();
    • 手动遍历(manual operation):

while (iterator.hasNext()) {

System.out.print(iterator.next().toUpperCase() + " ");

}

图片吐槽点:这样写能工作(It works),但写 while 循环感觉麻烦(feels like a lot of work)。

10. 6 next() 的关键概念( Crucial concept

    • next() 会返回当前元素(returns the current element)
    • 并且会把迭代器的"游标"向前移动(advances the iterator cursor forward)

这解释了为什么 hasNext()/next() 必须配合使用:

    • hasNext() 先判断还有没有
    • next() 再取出并前进

10.7 foreach 循环( foreach loop ):让你 " 偷懒 " do be lazy

对比:

Before (手动 manual

while (iterator.hasNext()) { ... }

After foreach

for (String element : collection) { // Read as: "in"

System.out.print(element.toUpperCase() + " ");

}

特点(features):

    • foreach 更省代码(writes the code for you)
    • 适用于数组(arrays)和任何可迭代对象(any Iterable)

10.8 Java 8 forEach 方法( forEach method )与 Consumer Consumer action

collection.forEach(e ->

System.out.print(e.toUpperCase() + " "));

解释(explanation):

    • 这是使用 Consumer(消费者动作):把你的逻辑自动应用到每个元素(applies your logic to every element automatically)
    • "函数式思维(functional approach)":告诉集合做什么(WHAT),而不是怎么循环(HOW)

10.9 三种循环方式对比( Iterators vs Foreach vs forEach

对照表(loop comparison):

    • Iterator While Loop
      • 缺点:冗长(verbose)
      • 但在"循环中需要 remove()"时必须用(necessary if need remove during loop)
    • Foreach Loop
      • 最干净语法(cleanest syntax)
      • 推荐用于简单遍历(recommended for simple traversal)
      • 不能删除元素(cannot remove elements)
    • forEach Method
      • 函数式风格(functional style)
      • 一行很简洁(concise one-liners)

红色警告( critical producer note

如果你需要在遍历时删除元素(remove elements while looping),必须使用显式迭代器(explicit Iterator)。

    • List

11.1 List 接口( List interface ):有序 + 可重复 + 有下标

图片把 List 描述成更精确的蓝图(precise blueprint),它保证:

    • 有序(Ordered collection):元素按顺序存储(stored in sequence)
    • 允许重复(Duplicates allowed):可以存相同元素多次(store same element twice)
    • 位置控制(Positional control):可以在精确位置插入(exact insertion points)

核心方法(core methods):

    • add(index, element):按位置插入(insert at index)
    • get(index):按下标取(get by index)
    • indexOf(element):找元素位置(find index)
    • subList(from, to):取子列表(sublist)

11.2 ListIterator :双向导航器( bidirectional navigator

    • Iterator:只能向前(forward only ->)
    • ListIterator:双向(bidirectional <->)

接口定义(interface definition):

    • public interface ListIterator<E> extends Iterator<E>

关键方法(key methods):

    • 向前(forward):hasNext(), next(), nextIndex()
    • 向后(backward):hasPrevious(), previous(), previousIndex()
    • 修改(modify):add(e), set(e)

并提示:可以用 listIterator(startIndex) 从任意位置开始导航(begin navigation anywhere)。

(1) ArrayList Gadget #1: ArrayList

机制(mechanism):

    • 可变长数组(resizable-array implementation)

行为(behavior):

    • 容量自动增长(capacity grows automatically)
    • 满了会创建更大数组并复制旧数据(new larger array + copy old data)

构造器(constructors):

    • new ArrayList():默认容量策略
    • new ArrayList(capacity):预设容量(pre-sized)

内存提示(memory tip):

    • 可用 trimToSize() 把容量收缩到当前大小(shrink capacity to fit current size)

(2) LinkedList Gadget #2: LinkedList

机制(mechanism):

    • 双向链表(doubly-linked list)

"双重身份"(dual nature):

    • 同时实现 List 和 Deqeue(double ended queue)

Deque 特有方法(exclusive Deque methods):

    • addFirst(e), addLast(e)
    • getFirst(), getLast()
    • removeFirst(), removeLast()

图片比喻:像一条链(like a chain),两端操作很容易(snap links on/off ends easily)。

代码演示( In Action: Code Walkthrough

图片示例步骤:

    • List<Integer> arrayList = new ArrayList<>();
    • arrayList.add(1);:自动装箱(Autoboxing: int -> Integer)
    • arrayList.add(2);
    • arrayList.add(0, 10);:在 index 0 插入(insert at index 0)

"魔法转换"(magic conversion / copy constructor):

    • LinkedList<Object> linkedList = new LinkedList<>(arrayList);
      说明 LinkedList 可以用"拷贝构造器(copy constructor)"从另一个集合初始化

然后:

    • linkedList.add(1, "red");
    • linkedList.removeLast();

控制台输出(console output)展示:

    • Step 1: 10, 1, 2
    • Step 2 (Conversion): 10, red, 1

12) ArrayList vs LinkedList :效率对决( Efficiency showdown

12.1 随机访问( Random Access: get/set

    • ArrayList:get/set 快(Fast ****O(1)****constant time,像"瞬移 teleportation via index")
    • LinkedList:get/set 慢(Slow O(n) linear time,必须沿链走 must walk the chain)
      结论(verdict):频繁读/访问(frequent reading/access)用 ArrayList。

12.2 插入 / 删除( Modification: insert/delete

    • ArrayList:慢(slow),因为需要移动元素(requires shifting elements)
    • LinkedList:快(fast),只改指针(pointer change only)
      结论:在开头/结尾大量操作(heavy manipulation at start/end,如 stacks/queues)用 LinkedList。

12.3 效率陷阱( Efficiency trap

图片警告:

    • 在 LinkedList 上用 for (i=0; i<size; i++) list.get(i) 非常慢(very slow)
    • 因为每次 get(i) 都是 O(n),整体变成 O(n^2)(quadratic time)

推荐(good):

    • 用 foreach(implicit iterator):for (Integer x : list) { process(x); }
      这样整体是 O(n)(linear)。

13) 快速创建 List Quick creation methods

    • Arrays.asList(T... a):返回由数组支撑的固定大小列表(fixed-size list backed by an array)
    • List.of(T... elements)(Java 9+):不可变列表工厂(immutable list factory),直观简洁(intuitive and concise)

14) 选择速查表( Cheat sheet: which to choose

    • 结构(Structure)
      • ArrayList:可变长数组(Resizable array)
      • LinkedList:链式节点(Linked nodes)
    • 随机访问(Random access / get)
      • ArrayList:快 O(1)(Winner)
      • LinkedList:慢 O(n)
    • 开头插入(Insert at start)
      • ArrayList:慢(Shifting)
      • LinkedList:快 O(1)(Winner)
    • 内存开销(Memory overhead)
      • ArrayList:低(Low)
      • LinkedList:高(High,node objects)

最终建议(Doraemon's final advice):

    • 不确定就先用 ArrayList(start with ArrayList)
    • 只有当你确实需要在开头高效增删(efficient adding/removing from the beginning)时再换 LinkedList。

15) Comparable vs Comparator Comparable 对比 Comparator

场景:你想排序(sort)geometricObjects,但写:

    • Collections.sort(geometricObjects);
      会报错:Not Comparable(因为 GeometricObject 没有自然顺序)。

图片解释:

    • GeometricObject 没有自然顺序( no natural order ,需要一个外部裁判(external judge):Comparator

对比表(table):

    • Comparable:内部(internal),由类自己实现(implemented by the class),定义自然顺序(defines natural order)。
    • Comparator :外部(external),单独的类(a separate class),定义自定义顺序(defines custom order)。
      提示:Comparator 更灵活(flexibility)。

16) Comparator 接口( The Comparator Interface

图片把 Comparator 当成"排序机器(sorting machine)",接口形状是:

    • interface Comparator<T> { int compare(T o1, T o2); }

比较结果含义(return meanings):

    • 负数(negative)→ A < B
    • 0(zero)→ A == B
    • 正数(positive)→ A > B

图片给的数学技巧(math trick):

    • compare(a, b) ≈ a - b(用差值判断大小)

17) " 经典 " 写法:单独写一个比较器类( Classic implementation

图片示例文件:GeometricObjectComparator.java(单独一个文件)。

    • public class GeometricObjectComparator implements Comparator<GeometricObject>, java.io.Serializable
    • compare(GeometricObject o1, GeometricObject o2):取面积(area1 = o1.getArea(),area2 = o2.getArea()),然后:
      • area1 < area2 → return -1
      • area1 == area2 → return 0
      • else → return 1
        图片标注:Best Practice(最佳实践)------清晰、规范,但写起来比较啰嗦(verbose)。

18) " 裁判 " 插到 max 里( Plugging in the Judge

图片把 max() 比作机器:它不懂形状(doesn't know shapes),所以把 Comparator 传进去教它怎么判断(teach it how to judge)。

示例:

    • GeometricObject g1 = new Rectangle(5, 5);
    • GeometricObject g2 = new Circle(5);
    • GeometricObject g = max(g1, g2, new GeometricObjectComparator());
      输出提示:大对象的面积(area of the larger object)是多少。

核心点:

    • **Comparator 作为参数(parameter)**让通用方法(generic method like max)可以处理"无自然顺序"的对象。

19) 啰嗦陷阱( Verbose Trap → Java 8 Lambda Lambda Expressions

图片左边:不想新建文件,就写匿名内部类(anonymous inner class):

new Comparator<String>() {

public int compare(String s1, String s2) {

return s1.length() - s2.length();

}

}

虽然不用新文件,但仍然难看(ugly to read)。

图片右边:解决方案(solution)

    • Java 8 开始,Comparator 是函数式接口(functional interface),可以用 Lambda(lambda expression)。

升级后的 Lambda 示例(Upgrade):

    • (o1, o2) -> o1.getArea() > o2.getArea() ? 1 : -1
      并强调三点:
    • 参数可推断(inferred parameters)
    • Lambda 运算符(lambda operator ->)
    • 更精简的逻辑体(concise logic body)

20) 自定义排序:字符串按长度( Custom Criteria: Sorting Strings

图片对比两种排序标准(criteria):

    • 自然顺序(natural order):按字母表(alphabetical)
    • 自定义 Comparator(custom comparator):按长度(by length)

代码示例:

    • cities 是 String\[\] 数组
    • Arrays.sort(..., comparator) 按你给的比较规则排序
    • Arrays.sort(cities, (s1, s2) -> s1.length() - s2.length());

并提示:

    • 如果 s1 更短(s1 shorter),s1.length() - s2.length() 为负数(negative)→ s1 排前面。

21) 语法糖:方法引用( Syntactic Sugar: Method References

图片给等价关系:

    • (s1, s2) -> s1.compareToIgnoreCase(s2)
      等价于
    • String::compareToIgnoreCase(method reference)

示例:

    • List<String> cities = Arrays.asList("Atlanta", "Savannah", "Dallas");
    • cities.sort(String::compareToIgnoreCase);

要点:方法引用让你不用显式写参数传递(let compiler do the work)。

22) " 终极工具 " Comparator.comparing Comparator.comparing

图片把 Comparator.comparing() 描述成"读起来像英语(reads like English)":

    • Arrays.sort(cities, Comparator.comparing(String::length));

关键词:

    • Key Extractor (键提取器) :String::length
      意思是:按"某个属性(property)/键(key)"来比较。

23) 真实例子:排序贷款( Real World: Sorting Loans

图片示例:Loan 有属性 amount、interestRate。

排序代码:

    • Arrays.sort(list, Comparator.comparing(Loan::getLoanAmount));

解释:Key Extractor 决定比较依据(judge the loans based on this specific property)。

24) 相同键如何打破平局( Breaking ties with thenComparing

问题:如果两个 loan 金额相同(same amount),谁先?

做法:链式比较(chaining comparisons):

    • Comparator<Loan> loanComparator = Comparator.comparing(Loan::getLoanAmount)
      .thenComparing(Loan::getAnnualInterestRate);

逻辑:

    • 先比金额(compare loan amount)
    • 如果相等(equal)再比利率(compare interest rate)

25) 反转排序( Flipping the script: reversed

想要降序(descending order),不必交换参数或手动取负(no need to swap args / write negative math),直接:

    • Arrays.sort(list, Comparator.comparing(Loan::getLoanAmount).reversed());

26) 哆啦 A 梦总结表( Doraemon's Cheat Sheet

图片表格总结"从旧到新"的 comparator 写法:

    • 老方法(The Old Way):class MyComp implements Comparator → 啰嗦、独立文件(verbose, separate file)
    • Lambda(The Lambda):(o1, o2) -> ... → 简洁、内联(concise, inline)
    • 现代方法(The Modern Way):Comparator.comparing(Class::prop) → 可读、声明式(readable, declarative)
    • 强力工具(Power Tools):.thenComparing(), .reversed() → 链式逻辑(chaining logic)

最后提示:别重复造轮子(don't reinvent the wheel),用 Collections的静态工具方法(static utility methods)。

注意:java.util.Collections (工具类 utility class)不要和 Collection interface(集合接口)搞混。

A) Collections 工具类( java.util.Collections "Gadgets" 合集

Gadget #1 :即时排序器( Instant Sorter

    • 代码思路:把一组字符串(strings)放进列表(List),然后用 Collections.sort(list) 自动按字母顺序排序(alphabetical / natural order)。
    • 图片感叹:居然会自动按字母排序(sorted alphabetically automatically)。

关键词:排序(sort)、自然顺序(natural order)、Collections.sort

Gadget #2 :反转器( Reverser

    • 场景:如果想要倒序(reverse order / descending),使用:
      • Collections.sort(list, Collections.reverseOrder());
    • Collections.reverseOrder():返回一个反转自然顺序的比较器(Comparator that reverses natural ordering)。
    • 输出示例显示:列表被按相反顺序排列。

关键词:反向比较器(reverseOrder)、比较器(Comparator)、倒序排序(descending sort)

Gadget #3 :高速查找器(二分查找 binarySearch

    • 方法:Collections.binarySearch(list, key)
    • 强制前提( WARNING :列表必须先排好序(List must be sorted FIRST)。
    • 找到时:返回索引(returns index),示例 search(...,7) 返回 index 2。
    • 找不到时:返回负数(negative result),并遵循公式:
      Not Found Logic :-(insertion point) - 1
      图片示例:查 9 的插入点(insertion point)是 index 3 → -(3) - 1 = -4。

关键词:二分查找(binarySearch)、插入点(insertion point)、必须先排序(sorted first)

Gadget #4 :搅拌器( Mixer

包含两个操作:

4.1 Collections.reverse() (反转 reverse

只翻转顺序(flips order only),不是随机。

4.2 Collections.shuffle() (洗牌 shuffle

    • 随机打乱顺序(randomizes order)。
    • 还支持带种子(seeded)的版本:
      • Collections.shuffle(list, new Random(20));
        图片提示:用种子(seed)可以让结果可复现(reproducible),适合一致性测试(consistent testing)。

关键词:反转(reverse)、洗牌(shuffle)、随机种子(seed)、可复现(reproducible)

Gadget #5 :克隆器( Cloner / copy

    • 方法:Collections.copy(dest, src)
    • 图片给了"陷阱(trap)":
      • 如果目标列表 dest 的长度比源列表 src 小,会抛 IndexOutOfBoundsException。
      • 示例:src size=2,dest size=0(空)→ copy 直接报错。
    • 解决(solution):dest 的大小必须 ≥ src(Destination must be >= Source size)。
      图片示例用"先创建足够长度的 dest"再 copy 成功。

关键词:拷贝(copy)、目标大小(destination size)、越界异常(IndexOutOfBoundsException)

Gadget #6 :复制器( Replicator

两种功能:

6.1 Collections.nCopies(n, value) (创建 creation

    • 创建一个含 n 个相同元素的列表(n copies)。
    • 图片标注:结果是不可变(immutable / read-only)------不能修改返回列表。

6.2 Collections.fill(list, value) (修改 modification

    • 把已有列表所有元素替换成同一个值(replaces content of an existing list)。

关键词:nCopies(nCopies)、不可变(immutable)、填充(fill)

Gadget #7 :分析器( Analyzer max / min

    • Collections.max(nums):按自然顺序找最大(find highest value)。
    • 自定义比较:用 Comparator 找"最短字符串"(shortest length),例如:
      • Collections.min(strings, Comparator.comparing(String::length));
    • 图片强调:不用手写循环(No loops),一行代码搞定(one line of code)。

关键词:最大值(max)、最小值(min)、Comparator.comparing(comparing)

Gadget #8 :检查器( Inspector

8.1 频率扫描( Frequency Scanner

    • Collections.frequency(list, "red"):统计元素出现次数(count occurrences)。
    • 图片示例显示结果为 2。

8.2 不相交检测( Disjoint Detector

    • Collections.disjoint(list1, list2):判断两个集合是否没有共同元素(no common elements)。
    • 输出:不相交返回 true(disjoint true),有交集返回 false(disjoint false)。

关键词:频率(frequency)、不相交(disjoint)、交集(overlap)

Gadget Inventory (工具清单)

图片把工具分组:

    • 排序(SORTING):sort、reverseOrder
    • 搜索(SEARCHING):binarySearch(必须先 sort)
    • 修改(MODIFYING):shuffle、reverse、copy、fill
    • 分析(ANALYZING):max、min、frequency、disjoint
    • 创建(CREATING):nCopies

Mission Accomplished (总结)

图片 checklist:

    • 效率(Efficiency):使用优化过的方法(optimized code)
    • 可读性(Readability):标准方法大家都认识(standard methods everyone knows)
    • 安全性(Safety):注意 copy 的大小要求(copy size)和 binarySearch 的排序要求(sort requirements)
      最后一句:去实验里练(practice in the lab)。

B) 特殊容器: Vector & Stack Specialized Containers

1) Vector (向量 Vector

    • 特点:同步(synchronized / thread-safe),即同一时刻只有一个线程能修改(one thread at a time)。
    • 代价:更慢(slower than ArrayList)。
    • 建议:只有需要时才用(use only when necessary)。

关键词:同步(synchronized)、线程安全(thread-safe)、更慢(slower)

2) Stack (栈 Stack

    • 行为:后进先出(LIFO: Last-In, First-Out)
    • 常用方法:push()、pop()、peek()

C) 队列与优先队列( Queues & Priority Queues

1) Queue (队列 Queue

    • 行为:先进先出(FIFO: First-In, First-Out)
    • 方法:offer()、poll()、peek()

2) PriorityQueue (优先队列 PriorityQueue

    • 按重要性排序(ordered by importance)
    • 最高优先级先出(highest priority leaves first)
    • 与到达顺序无关(regardless of arrival time)

D) 旧时代容器( Legacy ): Vector & Stack 的历史

1) 历史( history

    • Vector 和 Stack 在 Java 2 之前就存在(before Java 2)。
    • 后来为适配 Java Collections Framework 被重新设计(redesigned)。
    • 仍保留旧式方法用于兼容(old-style methods for compatibility)。
    • 图示关系:AbstractList → ArrayList 与 Vector;Stack 基于 Vector(extends)。

E) Vector " 同步容器 " 对比( Vector vs ArrayList

    • ArrayList:不同步(not synchronized)→ 更快(faster)
    • Vector:同步(synchronized)→ 线程安全(thread-safe)但更慢(slower)
    • 关键概念(key concept):同步方法防止多线程访问时数据损坏(prevent data corruption in multithreading)。
    • 经验法则(rule of thumb):不需要同步就用 ArrayList,更高效(more efficient)。

F) Vector 的构造( Constructing a Vector

图片列了几种构造方式(constructors):

    • new Vector<>():默认(initial capacity 10)
    • new Vector<>(existingCollection):从现有集合创建(from existing collection / copy)
    • new Vector<>(20):指定容量(specific capacity)
    • new Vector<>(20, 5):容量 + 增量(capacityIncrement)
      • 独特特性:capacityIncrement 控制"满了之后增长多少"(controls growth when full)

G) Vector 的旧式操作( Legacy operations

图片表格对应:

    • addElement(o) ↔ add(o)
    • insertElementAt(o, index) ↔ add(index, o)
    • removeElement(o) ↔ remove(o)
    • removeAllElements() ↔ clear()

并指出:老 Vector API 常带 "Element" 后缀(suffix),而这些方法也是同步的(synchronized)。

H) 容量管理( Managing Capacity

图片说明 capacity(容量)和 size(实际大小)差异会造成"浪费内存"(wasted memory)。

    • capacity():返回当前总容量(current total space)
    • setSize(newSize):手动设置 size(manually sets size)
    • trimToSize():把 capacity 缩到当前 size(trim capacity to match size),节省内存(saves memory)

下面按你这组图片内容(主题:Stack Queue PriorityQueue LinkedList 作为 Queue Queue 方法安全性、 PriorityQueue 自然顺序与自定义优先级、 Vector/Stack 关系 )做一份紧贴图片的详细中文笔记,关键词后附英文。

27) Stack 类( The Stack Class

图片用"煎饼堆"解释栈(Stack):

    • 定义(Definition):Stack extends Vector(Stack 继承 Vector)。
    • 核心概念(Concept):LIFO(Last-In, First-Out)后进先出
    • 规则(Rule):只能拿走最后放上去的那个(the top one)。

对话强调:想拿底部(bottom)是不行的,必须先拿顶端(top)------这就是 LIFO。

a) 压栈与出栈( Push and Pop

图片给出两个最经典操作(core operations):

    • push(E item):压栈(push),加到栈顶(adds to top)
    • pop():出栈(pop),移除并返回栈顶(removes & returns top)

记忆:push 把盘子放上去;pop 把最上面拿走。

b) 查看与检查( Peeking and Checking

图片列出:

    • peek():查看栈顶(look at top)但不移除(without removing)
    • empty():是否空栈(is empty),空则返回 true(returns true if contains no items)

示例(example):

    • Stack<String> s = new Stack<>();
    • s.push("Dorayaki");
    • System.out.println(s.peek());
      输出 "Dorayaki",但栈大小仍是 1(stack size still 1)。

c) 栈搜索( Searching the Stack

图片强调 search(Object o) 的返回规则(return value rule):

    • 方法(Method):search(Object o)
    • 返回值(Return Value):从栈顶开始数的 1-based 位置(1-based position from the TOP)
    • 没找到返回 -1(returns -1 if not found)

⚠️ 注意(Note):它不是数组下标(not like array index),不是从 0 开始,而是从 1 开始,并且从"顶端往下数"。

28) 该用哪个容器( Which Gadget to Use?

    • ArrayList
      • 用途(Use):通用(general purpose)、速度(speed)
      • 注意(Note):非线程安全(not thread-safe)
    • Vector
      • 用途(Use):旧代码(legacy code)、简单线程安全(simple thread safety)
      • 注意(Note):更慢(slower)
    • Stack
      • 用途(Use):LIFO 逻辑(Last-In, First-Out logic)
      • 注意(Note):继承 Vector(extends Vector)

旁边提示:现代 Java 一般优先 ArrayList,但理解 Vector/Stack 有助于读旧代码(legacy code)。

29) Queue PriorityQueue Queues and Priority Queues in Java

图片把队列(Queue)比作排队买铜锣烧:

(1) Queue FIFO

    • FIFO:First-In, First-Out 先进先出
    • 定义(Definition):元素从末尾加入(appended to the end),从开头移除(removed from the beginning)。
    • 图上标注操作:
      • 插入(Insertion):offer(从队尾进)
      • 移除(Removal):poll(从队头出)

直观:先来的先走(first to arrive is first to leave)。

(2) Queue Java 里是接口( Queue is an interface

    • Queue 不是一个类(not a class),而是接口(interface)。
    • 一般用 LinkedList 来"真正实现"(concrete implementation)一个队列。
    • 继承层次(Hierarchy):
      • Queue interface extends java.util.Collection
      • 它增加了插入/取出/查看(insertion, extraction, inspection)相关操作。
      • 常见实现(implementations):LinkedList、PriorityQueue。

(3) Queue 方法安全协议( Gadget Safety Protocols

把 Queue 方法分成两类:

3.1 安全方法( Safe methods --- 空队列不会崩( returns null/false

    • offer(e)(insert)
    • poll()(remove)
    • peek()(examine)

特点:队列为空(empty)时不会抛异常(won't crash),通常返回 null 或 false(returns null/false)。

3.2 风险方法( Risky methods --- 可能抛异常( throws exception

    • add(e)(insert)
    • remove()(remove)
    • element()(examine)

图片建议:尽量用 safe 方法(use offer/poll/peek),避免空队列崩溃。

(4) LinkedList 作为 Queue LinkedList is special

图片强调 LinkedList 的"双接口身份":

    • LinkedList 同时实现 List 和 Queue(implements both List and Queue)。
    • 它在两端增删(adding/removing from the ends)很高效(very efficient)。

示例代码(example):

Queue<String> queue = new LinkedList<>();

queue.offer("Oklahoma");

queue.offer("Indiana");

queue.offer("Georgia");

queue.offer("Texas");

(5) 队列处理( Queue Processing / FIFO

图片用管道演示:元素按加入顺序被取出(retrieved in the exact order they were added)。

示例循环(loop):

    • while (queue.size() > 0) { System.out.print(queue.remove() + " "); }
      输出按 FIFO:Oklahoma → Indiana → Georgia → Texas(先入先出)。

(6) 转折:优先队列( The Twist: Priority Queues

图片引入"VIP 插队":

    • PriorityQueue(优先队列):不是按到达顺序(arrival time),而是按优先级/自然顺序(priority / natural ordering)处理。
    • 规则(rule):更小的元素优先(smaller elements come first)
      • 对数字:1 比 10 优先
      • 对字符串:字母序更靠前(A before Z)优先

图片总结:默认最小元素是 VIP(smallest item removed first)。

(7) PriorityQueue 示例:按字母序( Alphabetical priority

示例(example):

PriorityQueue<String> queue1 = new PriorityQueue<>();

queue1.offer("Oklahoma");

queue1.offer("Indiana");

queue1.offer("Georgia");

queue1.offer("Texas");

while (queue1.size() > 0) System.out.print(queue1.remove() + " ");

输出变成按字母序(alphabetical order):Georgia, Indiana, Oklahoma, Texas(图片里强调 "Alphabetically first = Highest Priority")。

(8) 自定义优先级( Customizing Priority

如果你希望"最大的数字(largest number)"或"Z 优先"(highest number / Z first),图片说:

    • 把一个 Comparator(比较器)传给 PriorityQueue 的构造器(constructor)。
    • 示例使用 Collections.reverseOrder() 反转规则(reverse the order)。

代码思路(picture idea):

PriorityQueue<String> queue2 = new PriorityQueue<>(4, Collections.reverseOrder());

效果:优先级反转(priority reversed),最大的/字母序最后的先出来(largest comes out first)。

(9) 选择: LinkedList vs PriorityQueue

    • LinkedList (as Queue):逻辑 FIFO(fairness),适合普通排队(standard lines)。
    • PriorityQueue:按重要性(importance),适合 VIP 任务/调度(VIP tasks / scheduling)。

(10) PriorityQueue 重要安全规则( Important Safety Rules

图片列出 4 条(非常适合背):

    • 不允许 null(No nulls):PriorityQueue 不允许 null 元素(does not allow null elements)。
    • 必须可比较(Comparable required):元素必须实现 Comparable,或你提供 Comparator。
    • 不满足会崩(The crash):如果不可比较,会抛 ClassCastException。
    • 平局无保证(Ties arbitrary):优先级相同,顺序可能是任意的(order is arbitrary)。

TLDR

0. 文档定位( TL;DR purpose

  • 这份 TL;DR(TL;DR)用条目方式总结第5周核心概念(core concepts)、常用方法(common methods)与选型建议(choosing data structures)。
  • 覆盖:集合层次(hierarchy)、遍历(traversal)、List/Queue/Stack/PriorityQueue 特性、比较与排序(comparison & ordering)、Collections 工具方法(utility methods)、以及快速提醒(quick reminders)。

1. 核心概念与层次( Core Concepts and Hierarchy

  • Collection(Collection)是"元素集合"的根接口(root interface),覆盖 List/Queue/PriorityQueue/Stack/Set 共享的通用操作(common operations)。
  • Map(Map)与 Collection 分离(separate),存储键值对(key/value pairs)。
  • List(List):有序(ordered)且允许重复(allows duplicates)。
  • Stack(Stack):后进先出(LIFO, last-in first-out)。
  • Queue(Queue):先进先出(FIFO, first-in first-out)。
  • PriorityQueue(PriorityQueue):按优先级移除(removes by priority),不是按插入顺序(not insertion order)。
  • 框架结构(framework structure):接口(interfaces)+ 便利抽象类(convenience abstract classes)+ 具体类(concrete classes),大致:
    Collection → List / Queue → ArrayList / LinkedList / Vector / Stack / PriorityQueue。
  • 多数具体集合类(concrete collection classes)是可克隆(Cloneable)且可序列化(Serializable);PriorityQueue 是 Cloneable 的例外(exception)。

2. 遍历机制( Traversal Mechanisms

2.1 iterator iterator()

  • iterator():获取迭代器(gets an iterator)。
  • hasNext():判断是否还有元素(checks whether more elements remain)。
  • next():返回下一个元素(returns the next element)。
  • remove():删除"上一次 next() 返回的元素"(removes the last element returned by next)

2.2 foreach Foreach loop

  • foreach 循环(foreach loop):最简单的只读遍历(simplest read-only traversal),适用于

任何 Iterable(any Iterable)。

2.3 forEach(action) Java 8

  • forEach(action):对每个元素执行动作(performs an action on each element)。
  • 例:list.forEach(s -> System.out.print(s.toUpperCase() + " "));

3. 常见集合操作( Common Collection Operations

  • add(e):添加一个元素(adds one element)。
  • addAll(c):添加另一个集合所有元素(adds all elements from another collection)。
  • contains(o):判断是否存在(tests whether an element exists)。
  • containsAll(c):判断是否包含另一个集合的全部元素(tests whether all elements exist)。
  • remove(o):删除匹配元素(removes a matching element)。
  • removeAll(c):删除所有也出现在 c 的元素(removes elements also found in another collection)。
  • retainAll(c):只保留共同元素(keeps only common elements)。
  • clear():清空(removes everything)。
  • size():元素数量(returns number of elements)。
  • isEmpty():是否为空(checks whether collection has no elements)。
  • toArray():返回 Object 数组(returns an Object\[\])。
  • toArray(T\[\]):返回指定类型数组(returns a typed array)。
  • new String0:让 toArray 自己创建合适大小
  • new Stringlist.size():你提前创建好,toArray 直接填充

4. List 专有特性( List-Specific Features

  • List 是有序(ordered)、允许重复(duplicates)、支持基于索引访问(index-based access)。
  • add(index, e):在位置插入(inserts at a position)。
  • addAll(index, c):在位置插入集合(inserts a collection at a position)。
  • get(index):按下标读取(reads by index)。
  • set(index, e):替换并返回旧值(replaces and returns old element)。
  • remove(index):按下标删除(removes by index)。
  • indexOf(o) / lastIndexOf(o):首次/最后一次出现位置(first/last matching index)。
  • subList(from, to):返回视图(view),范围到 to-1(to - 1)。
  • ListIterator:从末尾开始可反向遍历(backward traversal),用 hasPrevious()/previous()。
  • trimToSize():缩减 ArrayList 容量到当前 size(shrinks capacity)。
  • LinkedList 支持两端操作(both ends efficiently):addFirst/addLast/getFirst/getLast/removeFirst/removeLast。
  • Arrays.asList(...):快速创建列表(quick create list)。

5. Queue Stack PriorityQueue Queue, Stack, PriorityQueue

5.1 Stack Stack

  • Stack 是旧式 LIFO 结构(legacy LIFO),建立在 Vector 上(built on Vector)。
  • push(e):压栈(adds to top)。
  • peek():看栈顶不删除(reads top without removing)。
  • pop():删除并返回栈顶(removes & returns top)。
  • empty():是否空栈(checks whether empty)。
  • search(o):返回元素在栈中的位置(position)。

5.2 Queue Queue

  • Queue 是 FIFO(FIFO);LinkedList 可实现 Queue(LinkedList implements queue)。
  • offer(e):推荐插入方式(preferred insert)。
  • poll():移除队首,空则返回 null(returns null if empty)。
  • remove():移除队首,空则抛异常(throws exception if empty)。
  • peek():读队首,空则返回 null(returns null if empty)。
  • element():读队首,空则抛异常(throws exception if empty)。

5.3 PriorityQueue PriorityQueue

  • PriorityQueue 先移除"最高优先级"(highest-priority)元素;默认用自然顺序(natural order),所以最小元素先出(least element comes out first)。
  • 自定义优先级:PriorityQueue(initialCapacity, comparator)(custom priority order)。
  • 例:new PriorityQueue<>(4, Collections.reverseOrder())(反向顺序 reversed order)。

6. 比较与排序( Comparison and Ordering

  • Comparable 定义自然顺序(natural order);Comparator 定义自定义顺序(custom order)。
  • compareTo(...):自然排序(natural ordering)。例:"Atlanta".compareTo("Dallas")。
  • compare(a,b):用 comparator 自定义排序(custom ordering)。
  • 例:Comparator<String> c = (s1, s2) -> s1.length() - s2.length();
  • sort(comparator):用指定 comparator 排序(sort with comparator)。例:list.sort(String::compareToIgnoreCase)。
  • sort(null):按自然顺序排序(natural order)。
  • Comparator.comparing(...):从 key 构建 comparator(build comparator from key)。例:Comparator.comparing(String::length)。
  • thenComparing(...):添加第二比较规则(secondary rule)。
  • reversed():反转 comparator 顺序(reverse comparator order)。

7. Collections 工具方法( Collections Utility Methods

  • Collections.sort(list):按自然顺序排序(natural order)。
  • Collections.sort(list, c) 与 reverseOrder():用 comparator 排序,常用于降序(descending)。
  • binarySearch(list, key):在已排序列表中二分查找(search a sorted list)。
  • binarySearch(list, key, c):用同一个 comparator 查找(same comparator as sorting)。
  • reverse(list):反转当前顺序(reverse current order)。
  • shuffle(list):随机打乱(random reordering)。
  • shuffle(list, random):固定 Random 可复现(reproducible shuffle)。
  • copy(dest, src):按索引拷贝(copies by index),dest 必须足够长(destination must be long enough)。 用 src 的元素去覆盖 dest 相同索引位置的元素
  • nCopies(n, o):生成不可变重复列表(immutable list of repeated references)。
  • fill(list, o):把列表全部替换成 o(replaces all elements)。
  • max(c) 与 min(c, comparator):最大/最小(largest/smallest)。
  • disjoint(c1, c2):是否无交集(no shared elements)。
  • frequency(c, o):出现次数(count occurrences)。

8. 如何选数据结构( Choosing the Right Data Structure

  • ArrayList:当索引随机访问(random access by index)很重要,且不频繁在前端插入/删除(not frequently insert/remove at front)。
  • LinkedList:当经常在开头或两端插入/删除(frequent insertion/removal at beginning or both ends)。
  • Vector:主要用于旧代码或需要同步(legacy / synchronized);否则 ArrayList 更快(faster)。
  • Stack:用于 LIFO 场景(LIFO tasks),如表达式求值(expression evaluation)。
  • Queue:用于 FIFO 处理(FIFO processing)。
  • PriorityQueue:移除顺序由优先级决定(removal by priority),不是到达顺序(arrival order)。
  • 若不需要插入/删除(no insertion/deletion),普通数组(plain array)比 list 更高效(more efficient)。

9. 快速提醒( Quick Reminders

  • List 允许重复(duplicates);Set 不允许(no duplicates)。
  • Collection 是元素集合的根(root);Map 是分离的(separate)。
  • LinkedList get(i) 能用但遍历很慢(inefficient traversal),应优先 foreach/forEach/iterator。
  • Queue:poll()/peek() 空时返回 null;remove()/element() 空时抛异常(throws exceptions)。
  • 默认 PriorityQueue:最小元素优先级最高(least element highest priority)。
  • PriorityQueue 不是 FIFO,除非优先级顺序刚好等于插入顺序(not FIFO unless matches)。
  • Collections.binarySearch 只在"已按相同顺序排序"的列表才有意义(must be sorted in matching order)。
  • Collections.copy 是浅拷贝(shallow),且 dest 需要足够槽位(enough slots)。
  • Collections.nCopies 返回不可变列表(immutable),且所有元素引用同一个对象(same object reference)。
  • 某些具体子类可能不支持某些操作(unsupported operations),会抛 UnsupportedOperationException。
  • Vector 方法是同步的(synchronized);若不需要同步,通常选 ArrayList(preferred)。

Lab Sheet

文档概览( Overview

  • 本实验单对应第5周主题:List、Queue、Stack、PriorityQueue。
    This lab matches Week 5 topics: List, Queue, Stack, PriorityQueue.
  • 学生重点:为每个问题选择合适的 Java 集合,并使用本周笔记介绍的核心方法
  • 四个任务是累积式(cumulative):要在同一个 todo 系统里从 Task1 扩展到 Task4,而不是写四个程序。

Task 1 :用 List 做核心任务列表( Core Task List with List

  • 场景:学生一天有许多小任务,需要按清晰顺序存储,方便查看和后续更新。
    Scenario: A student has many small tasks and needs them stored in a clear order for viewing and later updates.

必做功能( Required Features

  • 添加任务:用户输入标题(title),任务加入 todo list。
  • 查看全部任务:按顺序显示(in order),每条显示索引(index)、标题(title)、完成状态(completion status)。
  • 编辑任务:按索引选择(select by index),可更新标题(update title)。
  • 删除任务:按索引选择并从列表移除(removed from list)。
  • 标记完成:按索引选择并更新状态(status updated)。
  • 按状态显示:只看已完成(completed)或只看未完成(incomplete)。
  • 清晰遍历列表:显示任务时用 foreach / forEach / iterator 三者之一。

实现约束( Implementation Constraints

  • 任务必须用 List<Task> 存储,建议实现类如 ArrayList<Task>。
    Tasks must be stored using List<Task>; use a suitable implementation such as ArrayList<Task>.
  • 自定义 Task 类至少包含:title 与 completed。
    Define a custom Task class with at least: title and completed.
  • 编辑/删除等操作要用"基于索引"(index-based)的方法。

预期结果( Expected Outcome

  • 完成 Task1 后应能展示:有序存储(ordered storage)、索引访问与更新(index-based access/update)、遍历(traversal)、可扩展的基础结构(base structure)。

Task 2 :用 Queue 做任务处理流程( Queue-Based Task Processing

  • 场景:学生不会一次做完所有任务,日常更适合先进先出(FIFO)处理。
    Scenario: students don't do all tasks at once; FIFO processing suits day-to-day handling.
  • 目标:在保留 Task1 主列表的基础上,加一个处理队列(processing queue)。
    Goal: keep the original task list and add a processing queue on top.

必做功能( Required Features

  • 从主列表移入队列:用户从 todo list 选一个任务加入 queue,但任务仍属于整个系统(不丢失主列表结构)。
    Move from main list to queue: select a task, add to the queue, and keep it as part of the overall system.
  • 查看队列:按 FIFO 顺序显示所有排队任务。
    View queue: display all queued tasks in FIFO order.
  • 查看下一项:查看队首任务但不移除(without removing)。
    View next: display the front task without removing it.
  • 处理下一项:移除队首并显示刚处理了哪个任务。
    Process next: remove the front and display which task was processed.
  • 空队列处理:队列为空时给提示信息,不要崩溃(instead of crashing)。
    Handle empty queue: if empty, show a message instead of crashing.
  • 明确角色:主列表负责存储(stores tasks),队列负责 FIFO 处理(processes selected tasks)。
    Keep roles clear: list stores tasks; queue processes selected tasks in FIFO order.
  • 复用 Task1 菜单与数据模型:把队列功能加进原程序,不要新开项目。
    Reuse Task 1 menu and model: add queue options to the existing program, don't start a new one.

实现约束( Implementation Constraints

  • 队列必须用 Queue<Task>,建议具体类(concrete class)用 LinkedList<Task>。
    Queue must use Queue<Task>; use a concrete class such as LinkedList<Task>.
  • 使用 Week5 的队列操作:offer() 添加,peek() 查看队首,poll() 安全移除队首。
    Use Week 5 queue ops: offer() add, peek() inspect front, poll() safely remove front.
  • 不要替换 Task1 的 List<Task>,Queue 是额外组件(additional component)。
    Do not replace Task 1's List<Task>; the queue is an additional component.

预期结果( Expected Outcome

  • 能清楚展示 List 存储(storage)与 Queue 处理(processing)的差别。
    Demonstrate the difference between storing tasks in a List and processing tasks in a Queue.
  • 系统更像完整应用:既有存储也有工作流(workflow management)。
    System should feel more complete with both storage and workflow management.

Task 3 :用 Stack 记录最近操作( Stack-Based Recent Activity History

  • 场景:用户往往最想查看最近做了什么,符合后进先出(LIFO)模式。
    Scenario: users want to inspect most recent actions first; this matches LIFO and a stack.
  • 目标:在 Task1+Task2 的系统上新增"最近操作历史"(recent activity history)。
    Goal: extend the existing system by recording recent actions in a stack.

必做功能( Required Features

  • 记录操作:每次执行会改变状态的操作都记录简短描述(action description)。
    Record actions: whenever a state-changing operation occurs, record a short description.
  • 示例动作:ADD/EDIT/REMOVE/COMPLETE/MOVE_TO_QUEUE/PROCESS_QUEUE。
    Example actions: ADD, EDIT, REMOVE, COMPLETE, MOVE_TO_QUEUE, PROCESS_QUEUE.
  • 查看最近动作:查看栈顶但不移除(without removing)。
    View most recent: show latest action without removing it.
  • 移除最近动作:弹出(pop)并显示被移除的动作,体现 LIFO。
    Remove most recent: pop and display it to demonstrate LIFO behaviour.
  • 显示历史:从最新到最旧显示(most recent to oldest)。
    Display history: show recorded actions from most recent to oldest.
  • 空栈处理:无记录时给提示信息,不要崩溃。
    Handle empty stack: if none stored, show a message instead of crashing.
  • 解释理由:能说明为什么栈适合"最近动作历史"。
    Explain behaviour: describe why a stack suits recent-action history.
  • 保持前面功能可用:List 与 Queue 仍要正常工作,History 是新增不是替代。
    Keep previous features working: list and queue must continue; history is added, not a replacement.

实现约束( Implementation Constraints

  • 最近历史必须用 Stack 实现(implemented using Stack)。
    Recent action history must be implemented using Stack.
  • 可选:用 Stack<String> 存描述,或自定义 ActionRecord(含 action type、task title)。
    Options: Stack<String> or a small ActionRecord class (action type, task title).
  • 使用 Week5 栈操作:push() / peek() / pop()。
    Use Week 5 stack ops: push(), peek(), pop().

预期结果( Expected Outcome

  • 能展示 LIFO、peek vs pop 的差别,以及 stack 与 queue 处理差异。
    Demonstrate LIFO, the difference between viewing/removing the top, and how stack differs from queue.
  • 系统应包含:List 存储、Queue FIFO 处理、Stack 最近历史。
    System should include: List storage, FIFO Queue processing, Stack recent history.

Task 4 :优先级调度( Priority-Based Scheduling

  • 场景:任务紧急程度不同,FIFO 不够,需要优先级(priority)。
    Scenario: not all tasks are equally urgent; FIFO is insufficient when urgent tasks should be first.
  • 目标:在同一系统里保留 List、FIFO Queue、Stack,再新增 PriorityQueue 作为第二种处理策略。
    Goal: keep list, FIFO queue, and stack, then add a PriorityQueue as a second processing strategy.

必做功能( Required Features

  • 每个任务要有优先级(priority):如 HIGH/MEDIUM/LOW。
    Assign a priority to each task, e.g., HIGH, MEDIUM, LOW.
  • 把任务加入优先队列:用户选择任务加入 priority queue,并且要与 FIFO queue 并存(alongside)。
    Add tasks to priority queue alongside the normal FIFO queue.
  • 查看优先队列任务:显示当前在 priority queue 里的任务,并清晰显示优先级。
    View priority tasks: display tasks in the priority queue and show priority levels clearly.
  • 查看最高优先任务:查看下一次会被处理的任务但不移除。
    View highest-priority task: display next task without removing it.
  • 处理最高优先任务:移除并显示最高优先级任务。
    Process highest-priority task: remove and display the highest-priority task.
  • 对比 FIFO:能解释普通队列(normal queue)和优先队列(priority queue)的区别。
    Compare FIFO: explain the difference between a normal queue and a priority queue.
  • 空优先队列处理:空时显示提示信息。
    Handle empty priority queue: display an appropriate message if empty.
  • 系统概览:能查看主列表、FIFO 队列、优先队列、最近 10 条历史记录。
    Provide overview: view main list, FIFO queue, priority queue, and most recent 10 activities.

实现约束( Implementation Constraints

  • 使用 PriorityQueue<Task> 做优先级处理。
    Use PriorityQueue<Task> for priority-based processing.
  • 任务排序规则用 Week5 方法之一:Comparable<Task> 或 Comparator<Task>。
    Define ordering using Comparable<Task> or Comparator<Task>.
  • 若优先级相同:不要求按插入顺序取出(removal order need not match insertion order);若想加第二规则,可用标题字母序(alphabetical title order)。
    If same priority: removal order need not match insertion; optional secondary rule like alphabetical title order.
  • 继续使用同一个 Task 类,只是新增 priority 字段(priority field)。
    Continue using the same Task class, extended with a priority field.
  • 保持三种结构同程序共存:List<Task> 存储、Queue<Task> FIFO、Stack 历史。
    Keep structures in same program: List<Task> storage, Queue<Task> FIFO, Stack history.

预期结果( Expected Outcome

  • 能展示 PriorityQueue 的自动排序(automatic ordering)、Comparable/Comparator 的使用、FIFO 与优先级处理的差别。
    Demonstrate automatic ordering with PriorityQueue, use of Comparable/Comparator, and FIFO vs priority processing.
  • 最终系统应更完整:List 存、两种方式处理(FIFO 或 priority)、Stack 回看历史,且所有新功能都基于 Task1 的同一程序扩展。
    Final system: tasks stored in a list, processed by FIFO or priority, history via stack, and all features build on the same core program.

总结( Summary

  • 完成本实验后应理解:不同集合类型解决不同问题。
  • List:有序存储 + 索引访问(ordered storage & index-based access)。
    List: ordered storage and index-based access.
  • Queue:FIFO 处理(FIFO processing)。
  • Stack:LIFO 历史(LIFO history)。
  • PriorityQueue:按优先级处理(priority-based processing)。
  • 核心目标:做一个逐步扩展(progressively extended)的应用,并为每个新需求选择正确的数据结构(correct data structure)与方法(methods)。
    Main goal: build one progressively extended app and apply the correct data structure and methods for each requirement.
  • import java.util.ArrayList;
  • import java.util.Comparator;
  • import java.util.LinkedList;
  • import java.util.List;
  • import java.util.PriorityQueue;
  • import java.util.Queue;
  • import java.util.Scanner;
  • import java.util.Stack;
  • /**
  • * Week 5 Complete Todo System
  • * - List: store tasks (ordered, index-based)
  • * - Queue: FIFO processing
  • * - Stack: recent action history (LIFO)
  • * - PriorityQueue: priority-based processing
  • */
  • public class TodoSystemComplete {
  • private final Scanner input = new Scanner(System.in);
  • // Task 1: Core list storage (List)
  • private final List<Task> tasks = new ArrayList<>();
  • // Task 2: FIFO processing (Queue)
  • private final Queue<Task> processingQueue = new LinkedList<>();
  • // Task 3: Recent actions (Stack, LIFO)
  • private final Stack<ActionRecord> recentActions = new Stack<>();
  • // Task 4: Priority tasks (PriorityQueue)
  • // NOTE: PriorityQueue iteration order is NOT sorted order; only peek/poll are ordered by priority.
  • private final PriorityQueue<Task> priorityQueue = createPriorityQueue();
  • public static void main(String\[\] args) {
  • TodoSystemComplete system = new TodoSystemComplete();
  • system.run();
  • }
  • public void run() {
  • boolean running = true;
  • while (running) {
  • printMainMenu();
  • int choice = readInt("Choose an option: ");
  • switch (choice) {
  • case 1 -> addTask();
  • case 2 -> viewAllTasks();
  • case 3 -> editTask();
  • case 4 -> removeTask();
  • case 5 -> markTaskCompleted();
  • case 6 -> viewTasksByStatus();
  • case 7 -> moveTaskToQueue();
  • case 8 -> viewProcessingQueue();
  • case 9 -> viewNextQueueTask();
  • case 10 -> processNextQueueTask();
  • case 11 -> viewMostRecentAction();
  • case 12 -> removeMostRecentActionFromHistory();
  • case 13 -> viewRecentActionHistory();
  • case 14 -> assignPriorityToTask();
  • case 15 -> addTaskToPriorityQueue();
  • case 16 -> viewPriorityQueue();
  • case 17 -> viewHighestPriorityTask();
  • case 18 -> processHighestPriorityTask();
  • case 19 -> viewSystemOverview();
  • case 0 -> {
  • running = false;
  • System.out.println("Exiting Todo System.");
  • }
  • default -> System.out.println("Invalid option. Please try again.");
  • }
  • }
  • }
  • private void printMainMenu() {
  • System.out.println();
  • System.out.println("=== Todo System ===");
  • System.out.println("Task 1 - Core List Features");
  • System.out.println("1. Add task");
  • System.out.println("2. View all tasks");
  • System.out.println("3. Edit task");
  • System.out.println("4. Remove task");
  • System.out.println("5. Mark task as completed");
  • System.out.println("6. View tasks by status");
  • System.out.println();
  • System.out.println("Task 2 - Queue Features");
  • System.out.println("7. Move task to processing queue");
  • System.out.println("8. View processing queue");
  • System.out.println("9. View next queue task");
  • System.out.println("10. Process next queue task");
  • System.out.println();
  • System.out.println("Task 3 - Stack Features");
  • System.out.println("11. View most recent action");
  • System.out.println("12. Remove most recent action from history");
  • System.out.println("13. View recent action history");
  • System.out.println();
  • System.out.println("Task 4 - Priority Queue Features");
  • System.out.println("14. Assign priority to a task");
  • System.out.println("15. Add task to priority queue");
  • System.out.println("16. View priority queue");
  • System.out.println("17. View highest-priority task");
  • System.out.println("18. Process highest-priority task");
  • System.out.println("19. View system overview");
  • System.out.println("0. Exit");
  • }
  • // -------------------------
  • // Task 1: List features
  • // -------------------------
  • private void addTask() {
  • String title = readNonEmptyLine("Enter task title: ");
  • Task task = new Task(title);
  • tasks.add(task);
  • recordAction(ActionType.ADD, task, "Added a new task.");
  • System.out.println("Task added.");
  • displayTask(task, tasks.size() - 1);
  • }
  • private void viewAllTasks() {
  • System.out.println("=== All Tasks ===");
  • if (tasks.isEmpty()) {
  • System.out.println("No tasks available.");
  • return;
  • }
  • int index = 0;
  • for (Task task : tasks) {
  • displayTask(task, index);
  • index++;
  • }
  • }
  • private void editTask() {
  • int index = chooseTaskIndex();
  • if (index == -1) return;
  • Task task = tasks.get(index);
  • String oldTitle = task.getTitle();
  • String newTitle = readNonEmptyLine("Enter the new title: ");
  • task.setTitle(newTitle);
  • // IMPORTANT: if a task is already in priorityQueue, changing title/priority can break heap order.
  • refreshPriorityQueueEntry(task);
  • recordAction(ActionType.EDIT, task,
  • "Changed title from '" + oldTitle + "' to '" + newTitle + "'.");
  • System.out.println("Task updated.");
  • displayTask(task, index);
  • }
  • private void removeTask() {
  • int index = chooseTaskIndex();
  • if (index == -1) return;
  • Task task = tasks.remove(index);
  • // Remove from other structures too (keep system consistent)
  • boolean removedFromQueue = processingQueue.remove(task);
  • boolean removedFromPriorityQueue = priorityQueue.remove(task);
  • String details = "Removed task from main list.";
  • if (removedFromQueue) details += " Also removed from processing queue.";
  • if (removedFromPriorityQueue) details += " Also removed from priority queue.";
  • recordAction(ActionType.REMOVE, task, details);
  • System.out.println("Task removed.");
  • }
  • private void markTaskCompleted() {
  • int index = chooseTaskIndex();
  • if (index == -1) return;
  • Task task = tasks.get(index);
  • task.setCompleted(true);
  • recordAction(ActionType.COMPLETE, task, "Marked task as completed.");
  • System.out.println("Task marked as completed.");
  • displayTask(task, index);
  • }
  • private void viewTasksByStatus() {
  • System.out.println("1. View completed tasks");
  • System.out.println("2. View incomplete tasks");
  • int choice = readInt("Choose an option: ");
  • boolean targetCompleted;
  • if (choice == 1) targetCompleted = true;
  • else if (choice == 2) targetCompleted = false;
  • else {
  • System.out.println("Invalid option.");
  • return;
  • }
  • boolean found = false;
  • System.out.println(targetCompleted ? "=== Completed Tasks ===" : "=== Incomplete Tasks ===");
  • for (int i = 0; i < tasks.size(); i++) {
  • Task task = tasks.get(i);
  • if (task.isCompleted() == targetCompleted) {
  • displayTask(task, i);
  • found = true;
  • }
  • }
  • if (!found) System.out.println("No matching tasks found.");
  • }
  • // -------------------------
  • // Task 2: Queue features
  • // -------------------------
  • private void moveTaskToQueue() {
  • int index = chooseTaskIndex();
  • if (index == -1) return;
  • Task task = tasks.get(index);
  • if (processingQueue.contains(task)) {
  • System.out.println("That task is already in the processing queue.");
  • return;
  • }
  • // Use offer/peek/poll (safe Queue methods)
  • processingQueue.offer(task);
  • recordAction(ActionType.MOVE_TO_QUEUE, task, "Moved task into FIFO processing queue.");
  • System.out.println("Task added to processing queue.");
  • }
  • private void viewProcessingQueue() {
  • System.out.println("=== Processing Queue ===");
  • if (processingQueue.isEmpty()) {
  • System.out.println("The processing queue is empty.");
  • return;
  • }
  • int position = 1;
  • for (Task task : processingQueue) {
  • System.out.print(position + ". ");
  • displayTask(task);
  • position++;
  • }
  • }
  • private void viewNextQueueTask() {
  • Task task = processingQueue.peek(); // does not remove
  • if (task == null) {
  • System.out.println("The processing queue is empty.");
  • return;
  • }
  • System.out.println("Next queue task:");
  • displayTask(task);
  • }
  • private void processNextQueueTask() {
  • Task task = processingQueue.poll(); // removes head, returns null if empty
  • if (task == null) {
  • System.out.println("The processing queue is empty.");
  • return;
  • }
  • recordAction(ActionType.PROCESS_QUEUE, task, "Processed the next task from FIFO queue.");
  • System.out.println("Processed task:");
  • displayTask(task);
  • }
  • // -------------------------
  • // Task 3: Stack features
  • // -------------------------
  • private void viewMostRecentAction() {
  • if (recentActions.isEmpty()) {
  • System.out.println("No recent actions recorded.");
  • return;
  • }
  • System.out.println("Most recent action:");
  • System.out.println(recentActions.peek()); // peek = view top without removing
  • }
  • private void removeMostRecentActionFromHistory() {
  • if (recentActions.isEmpty()) {
  • System.out.println("No recent actions to remove.");
  • return;
  • }
  • ActionRecord removed = recentActions.pop(); // pop = remove top
  • System.out.println("Removed action from history:");
  • System.out.println(removed);
  • }
  • private void viewRecentActionHistory() {
  • System.out.println("=== Recent Action History ===");
  • if (recentActions.isEmpty()) {
  • System.out.println("No recent actions recorded.");
  • return;
  • }
  • // Print most-recent to oldest (top to bottom)
  • for (int i = recentActions.size() - 1; i >= 0; i--) {
  • System.out.println(recentActions.get(i));
  • }
  • }
  • private void recordAction(ActionType actionType, Task task, String details) {
  • recentActions.push(new ActionRecord(actionType, task, details));
  • }
  • // -------------------------
  • // Task 4: PriorityQueue features
  • // -------------------------
  • private void assignPriorityToTask() {
  • int index = chooseTaskIndex();
  • if (index == -1) return;
  • Task task = tasks.get(index);
  • PriorityLevel oldPriority = task.getPriority();
  • PriorityLevel newPriority = choosePriorityLevel();
  • task.setPriority(newPriority);
  • // IMPORTANT: If task already inside priorityQueue, reinsert to restore heap ordering.
  • refreshPriorityQueueEntry(task);
  • recordAction(ActionType.ASSIGN_PRIORITY, task,
  • "Changed priority from " + oldPriority + " to " + newPriority + ".");
  • System.out.println("Priority updated.");
  • displayTask(task, index);
  • }
  • private void addTaskToPriorityQueue() {
  • int index = chooseTaskIndex();
  • if (index == -1) return;
  • Task task = tasks.get(index);
  • if (priorityQueue.contains(task)) {
  • System.out.println("That task is already in the priority queue.");
  • return;
  • }
  • priorityQueue.offer(task);
  • recordAction(ActionType.ADD_TO_PRIORITY_QUEUE, task, "Added task to priority queue.");
  • System.out.println("Task added to priority queue.");
  • }
  • private void viewPriorityQueue() {
  • System.out.println("=== Priority Queue ===");
  • if (priorityQueue.isEmpty()) {
  • System.out.println("The priority queue is empty.");
  • return;
  • }
  • // NOTE: PriorityQueue iterator is NOT sorted. Copy to list then sort for display.
  • List<Task> orderedView = new ArrayList<>(priorityQueue);
  • orderedView.sort(
  • Comparator.comparingInt(Task::getPriorityValue).reversed()
  • .thenComparing(Task::getTitle, String.CASE_INSENSITIVE_ORDER));
  • int position = 1;
  • for (Task task : orderedView) {
  • System.out.print(position + ". ");
  • displayTask(task);
  • position++;
  • }
  • }
  • private void viewHighestPriorityTask() {
  • Task task = priorityQueue.peek();
  • if (task == null) {
  • System.out.println("The priority queue is empty.");
  • return;
  • }
  • System.out.println("Highest-priority task:");
  • displayTask(task);
  • }
  • private void processHighestPriorityTask() {
  • Task task = priorityQueue.poll();
  • if (task == null) {
  • System.out.println("The priority queue is empty.");
  • return;
  • }
  • recordAction(ActionType.PROCESS_PRIORITY_QUEUE, task, "Processed the highest-priority task.");
  • System.out.println("Processed highest-priority task:");
  • displayTask(task);
  • }
  • private void viewSystemOverview() {
  • System.out.println("=== System Overview ===");
  • System.out.println("Task count: " + tasks.size());
  • System.out.println("Queue size: " + processingQueue.size());
  • System.out.println("Recent action count: " + recentActions.size());
  • System.out.println("Priority queue size: " + priorityQueue.size());
  • int completedCount = 0;
  • for (Task task : tasks) {
  • if (task.isCompleted()) completedCount++;
  • }
  • System.out.println("Completed tasks: " + completedCount);
  • System.out.println("Incomplete tasks: " + (tasks.size() - completedCount));
  • if (!tasks.isEmpty()) {
  • System.out.println();
  • System.out.println("Main task list:");
  • viewAllTasks();
  • }
  • if (!processingQueue.isEmpty()) {
  • System.out.println();
  • System.out.println("FIFO queue:");
  • viewProcessingQueue();
  • }
  • if (!priorityQueue.isEmpty()) {
  • System.out.println();
  • System.out.println("Priority queue:");
  • viewPriorityQueue();
  • }
  • if (!recentActions.isEmpty()) {
  • System.out.println();
  • System.out.println("Most recent activities (up to 10):");
  • int shown = 0;
  • for (int i = recentActions.size() - 1; i >= 0 && shown < 10; i--) {
  • System.out.println(recentActions.get(i));
  • shown++;
  • }
  • }
  • }
  • // -------------------------
  • // Helpers
  • // -------------------------
  • private PriorityQueue<Task> createPriorityQueue() {
  • // Higher priority value first; tie-break by title (case-insensitive)
  • return new PriorityQueue<>(
  • Comparator.comparingInt(Task::getPriorityValue).reversed()
  • .thenComparing(Task::getTitle, String.CASE_INSENSITIVE_ORDER));
  • }
  • private void refreshPriorityQueueEntry(Task task) {
  • // If task exists in heap and its comparison fields changed, reinsert to maintain heap property.
  • if (priorityQueue.remove(task)) {
  • priorityQueue.offer(task);
  • }
  • }
  • private int readInt(String prompt) {
  • while (true) {
  • System.out.print(prompt);
  • String line = input.nextLine().trim();
  • try {
  • return Integer.parseInt(line);
  • } catch (NumberFormatException ex) {
  • System.out.println("Please enter a valid integer.");
  • }
  • }
  • }
  • private String readNonEmptyLine(String prompt) {
  • while (true) {
  • System.out.print(prompt);
  • String line = input.nextLine().trim();
  • if (!line.isEmpty()) return line;
  • System.out.println("Input cannot be empty.");
  • }
  • }
  • private void displayTask(Task task, int index) {
  • System.out.printf("%d %s | completed=%s | priority=%s%n",
  • index, task.getTitle(), task.isCompleted(), task.getPriority());
  • }
  • private void displayTask(Task task) {
  • System.out.printf("%s | completed=%s | priority=%s%n",
  • task.getTitle(), task.isCompleted(), task.getPriority());
  • }
  • private int chooseTaskIndex() {
  • if (tasks.isEmpty()) {
  • System.out.println("No tasks available.");
  • return -1;
  • }
  • viewAllTasks();
  • int index = readInt("Enter task index: ");
  • if (index < 0 || index >= tasks.size()) {
  • System.out.println("Invalid task index.");
  • return -1;
  • }
  • return index;
  • }
  • private PriorityLevel choosePriorityLevel() {
  • System.out.println("1. HIGH");
  • System.out.println("2. MEDIUM");
  • System.out.println("3. LOW");
  • int choice = readInt("Choose a priority: ");
  • return switch (choice) {
  • case 1 -> PriorityLevel.HIGH;
  • case 2 -> PriorityLevel.MEDIUM;
  • case 3 -> PriorityLevel.LOW;
  • default -> {
  • System.out.println("Invalid choice. Defaulting to LOW.");
  • yield PriorityLevel.LOW;
  • }
  • };
  • }
  • // -------------------------
  • // Nested model types
  • // -------------------------
  • private enum ActionType {
  • ADD, EDIT, REMOVE, COMPLETE,
  • MOVE_TO_QUEUE, PROCESS_QUEUE,
  • ASSIGN_PRIORITY, ADD_TO_PRIORITY_QUEUE, PROCESS_PRIORITY_QUEUE
  • }
  • private enum PriorityLevel {
  • HIGH(3), MEDIUM(2), LOW(1);
  • private final int value;
  • PriorityLevel(int value) { this.value = value; }
  • public int getValue() { return value; }
  • }
  • private static class Task {
  • private String title;
  • private boolean completed;
  • private PriorityLevel priority;
  • public Task(String title) {
  • this.title = title;
  • this.completed = false;
  • this.priority = PriorityLevel.LOW;
  • }
  • public String getTitle() { return title; }
  • public void setTitle(String title) { this.title = title; }
  • public boolean isCompleted() { return completed; }
  • public void setCompleted(boolean completed) { this.completed = completed; }
  • public PriorityLevel getPriority() { return priority; }
  • public void setPriority(PriorityLevel priority) { this.priority = priority; }
  • public int getPriorityValue() { return priority.getValue(); }
  • @Override
  • public String toString() {
  • return title + " | completed=" + completed + " | priority=" + priority;
  • }
  • }
  • private static class ActionRecord {
  • private final ActionType actionType;
  • private final Task task;
  • private final String details;
  • public ActionRecord(ActionType actionType, Task task, String details) {
  • this.actionType = actionType;
  • this.task = task;
  • this.details = details;
  • }
  • @Override
  • public String toString() {
  • String taskTitle = task == null ? "n/a" : task.getTitle();
  • return actionType + " | task=" + taskTitle + " | details=" + details;
  • }
  • }
  • }

Week 6

Mastering Sets and Maps

1 主题与目标( Title & Theme

    • 主题:掌握集合 Set(Sets)与映射 Map(Maps),用于"唯一元素(unique elements)"与"键值对(key-value pairs)"的高性能数据结构(high-performance data structures)。

2 为什么 List 不适合 " 查存在 " List is the wrong tool

    • 场景:用 Java 列表(List)去查"禁飞名单(No-Fly list)"是否存在某人,需要一个个顺序查(sequential search / linear search),在几百万条数据上会太慢(too slow)。
    • 结论:List 允许重复(duplicates)且查找依赖顺序扫描(sequential searching),不适合做"纯存在性检查(pure existence check)"。

3 Set 数据结构( The Set Data Structure

    • 核心规则:零重复( zero duplicates ------Set 严格保证不能加入重复元素(no duplicate elements)。
    • 目的:高速验证( high-speed verification ------专门为"高效存储与处理不重复元素(non-duplicate elements)"设计。
    • 对应场景:禁飞名单这种"只关心在不在"(existence only),用 Set 比 List 更合适(perfect fit)。

4 Map 数据结构( The Map Data Structure

    • 需求升级:如果不仅要知道"在不在",还要存储详细信息(detailed intel / profile data)。
    • Map 的字典式方法(dictionary approach):用键(key)快速查到值(value),实现快速检索(quick lookup)。
    • 强调:Map 是配对相关数据(pair related data)且无需顺序查找(without sequential searching)的高效结构(maximum efficiency)。

5 选型对比表( List vs Set vs Map

    • List(List)
      • 优势:按索引有序访问(ordered access by index)
      • 是否允许重复:是(allows duplicates: yes)
      • 最佳用途:存储有顺序的序列(storing sequences where order matters)
    • Set(Set)
      • 优势:无序但元素唯一(unordered, unique elements)
      • 是否允许重复:否(allows duplicates: no)
      • 最佳用途:快速检查是否存在(checking pure existence)
    • Map(Map)
      • 优势:键值字典查找(key-value dictionary lookup)
      • 是否允许重复:键唯一(unique keys only)
      • 最佳用途:用 ID/姓名查"详细档案"(retrieve profiles by key)

6 学习目标( Target Objectives

    • 掌握 Set(Master the Set):理解何时使用 HashSet、LinkedHashSet、TreeSet。
    • 掌握 Map(Master the Map):区分 Collection 与 Map,掌握 HashMap、LinkedHashMap、TreeMap。
    • 性能分析(Performance analytics):比较 Set vs List 的真实执行速度(execution speed)。
    • 实作应用(Practical applications):
      • 用 Set 统计 Java 关键字(count Java keywords using Sets)
      • 用 Map 统计文本词频(count word occurrences using Maps)
    • 高级工具(Advanced utility):使用 Collections 获取单例/不可变集合(singleton / unmodifiable)。
      • 单例(singleton):只有 1 个元素(one element)
      • Collections.singleton(x) 会返回一个 Set,里面只有 x

7 Set 实现: HashSet / LinkedHashSet / TreeSet Set Implementations

    • 本节聚焦:如何选择不同 Set 实现来获得最优性能(optimal performance)。

(1) Set 层次结构( Set Hierarchy

    • 结构:Set → AbstractSet → 三大实现:
      • HashSet:主打原始速度(raw speed),顺序不可预测(unpredictable order)
      • LinkedHashSet:主打"可追踪"(tactical tracking),保持插入顺序(insertion order)
      • TreeSet:主打"精确排序"(perfect precision),保持排序顺序(sorted order)
    • 共同核心:Set 的绝对规则是"不允许重复"(no duplicates allowed)。

(2) AbstractSet 的作用( AbstractSet tech specs

    • AbstractSet 继承 AbstractCollection(extends AbstractCollection),提供基础功能(base functionalities)。
    • 它提供了 equals() 与 hashCode() 的具体实现(concrete implementations)。
    • 技术点(tech fact):一个 set 的 hashCode 是其所有元素 hashCode 的"总和"(sum of hash codes)
    • 同时把 size() 与 iterator() 留给具体实现去做(left abstract for implementations)

(3) HashSet :去重靠 equals + hashCode dedup via equals/hashCode

    • HashSet 用 equals() + hashCode() 来判断重复(duplicate detection)。
    • 示例:往 HashSet<String> 连续 add 两次 "New York",最终只保存一份(only

one is stored)。

    • 重要结论:HashSet 去重规则取决于对象的 equals/hashCode 是否正确实现(correct equals/hashCode)

(4) HashSet 容量、负载因子与扩容( capacity, load factor, threshold

    • 默认初始容量(capacity)= 16(default 16)。
    • 默认负载因子(load factor)= 0.75(default 0.75)。
    • 阈值(threshold)= capacity × loadFactor = 16 × 0.75 = 12。
    • 当元素个数达到阈值(size reaches threshold),容量翻倍(capacity doubles),例如扩到 32。
    • 负载因子越低(lower load factor)→ 查找更快(faster searches),但更耗空间(more space used)。
      • 桶更空 → 冲突更少(fewer collisions) → 每个桶里链/树更短 → contains / get 平均比较次数更少 → 查找更快

(5) String hashCode 公式( String hashCode formula

    • 为减少碰撞(avoid collisions),需要更均匀散列(scatter evenly)。
    • 文档给出 String hashCode 经典公式:
      s0 * 31^(n-1) + s1 * 31^(n-2) + ... + s(n-1)(多项式 rolling hash)
    • 注释:不同对象也可能 hash 相同(hash collision possible),但设计目标是最小化(minimize collisions)以提升性能(optimal performance)。

(6) 集合战术: addAll / removeAll / retainAll Collection tactics

    • set1.addAll(set2):合并(merge / union-like)
    • set1.removeAll(set2):清除重叠(remove overlapping / difference-like)
    • set1.retainAll(set2):只保留共有(keep common / intersection-like)

(7) LinkedHashSet :保持插入顺序( insertion order

    • LinkedHashSet 使用链表结构记住插入顺序(linked-list to remember insertion order)。
    • 关键规格(key specs):
      • 继承 HashSet(extends HashSet)
      • 比 HashSet 稍慢(slightly slower)
      • 输出顺序 = 添加顺序(exact order they were added)

(8) TreeSet :保持排序顺序( sorted order

    • TreeSet 让一切"完美排序"(perfectly sorted)。
    • 关键规格(key specs):
      • 实现 SortedSet 和 NavigableSet(implements SortedSet, NavigableSet)
      • 元素必须可相互比较(mutually comparable)
      • 可以把"混乱的 set"直接转成 TreeSet:new TreeSet<>(hashSet)(convert from chaotic set)

(9) SortedSet 常用方法( SortedSet methods

    • first():返回最小元素(returns first / smallest),图示例返回 "Beijing"
    • last():返回最大元素(returns last / largest),图示例返回 "San Francisco"
    • headSet("New York"):获取目标前面的所有元素(everything before target),例:Beijing, London
    • tailSet("New York"):获取目标及之后的元素(target and after),例:New York, Paris, San Francisco
    • lower("P"):严格小于 P 的最大元素(largest < P),示例指向 "New York"
    • floor("P"):小于等于 P 的最大元素(largest ≤ P)
    • ceiling("P"):大于等于 P 的最小元素(smallest ≥ P)
    • higher("P"):严格大于 P 的最小元素(smallest > P),示例指向 "Paris"
    • pollFirst() / pollLast():移除并返回两端元素(remove & retrieve ends)

(11) TreeSet 自定义排序:注入 Comparator inject Comparator

    • 默认 TreeSet 用 Comparable(Comparable)排序(natural ordering)。
    • 如果要自定义规则(custom sorting rules),例如按几何对象面积(area)排序,就传入 Comparator(Comparator):
      • new TreeSet<>(new GeometricObjectComparator())
    • 关键提醒(Result / warning):如果比较器认为两个对象"相等"(comparator sees as equal),Set 会当成重复并拒绝(reject duplicate)。
      例如两个圆半径相同(same radius)→ comparator 返回 0 → TreeSet 只保留一个。

( 12) 三种 Set 总结矩阵( Tactical summary matrix

    • HashSet(HashSet)
      • 性能(performance):插入/删除最快(fastest insertion/removal)
      • 顺序(order):不维护顺序(no order maintained)
      • 适用(best for):通用(general use)
    • LinkedHashSet(LinkedHashSet)
      • 性能:稍慢(slightly slower)
      • 顺序:精确插入顺序(exact insertion order)
      • 适用:按时间/加入顺序跟踪(chronological tracking)
    • TreeSet(TreeSet)
      • 性能:插入/删除较慢(slower insertion/removal)
      • 顺序:完美排序(perfect sorted order)
      • 适用:有序取数与范围查询(ordered retrieval & range searches)

8. 性能模拟( Set vs List performance simulation

    • 主题:Set vs List 的性能对比模拟(performance simulation)。
    • 场景:处理大规模数据集(massive dataset),需要选择最快检索/清理方式(retrieve/clear fastest)。
    • 强调:要做"只留有用结果"的模拟(run simulation and see which leaves rest in dust),以验证选择的数据结构是否正确(choose correct data structure).

9. 模拟参数:生成数据集( Simulation Parameters: Generating the Dataset

    • 用 final int N = 50000; 生成 50,000 个连续整数(sequential integers)。
    • 用 List<Integer> list = new ArrayList<>();,循环 for (i=0;i<N;i++) list.add(i); 填充。
    • 用 Collections.shuffle(list) 打乱顺序(shuffle / randomize)。
    • 产生"打乱的主列表"(Shuffled Master List, N=50,000),并用同一份打乱数据 初始化五种结构(fair fight / same dataset):
      • HashSet
      • LinkedHashSet
      • TreeSet
      • ArrayList
      • LinkedList

核心:统一输入(same scrambled list)保证测试公平(fair comparison)。

10. 协议 Alpha :成员测试( Protocol Alpha: Membership Testing

    • 目的:测"查找是否存在"(membership test / contains)。
    • 做法:用 contains() 查找随机目标(random intel),目标范围到 2N(up to 2N = 100,000)。
    • 计时方式:
      • long startTime = System.currentTimeMillis();
      • 循环 for (i=0;i<N;i++) c.contains((int)(Math.random()*2*N));
      • 返回 System.currentTimeMillis() - startTime(elapsed time)。

11. 协议 Beta :数据移除( Protocol Beta: Data Removal

    • 目的:测"逐个删除元素"(removal test / remove)。
    • 做法:计时并循环删除:
      • long startTime = System.currentTimeMillis();
      • for (i=0;i<N;i++) c.remove(i);
      • 返回耗时(elapsed time)。

12. 模拟结果:性能对决( Simulation Results: Performance Showdown

表格给出两类测试:成员测试(Membership Test / contains)与移除测试(Removal Test / remove)。

    • HashSet:contains 约 20 ms;remove 约 27 ms
    • LinkedHashSet:contains 约 27 ms;remove 约 26 ms
    • TreeSet:contains 约 47 ms;remove 约 34 ms
    • ArrayList:contains 约 39,802 ms;remove 约 16,196 ms
    • LinkedList:contains 约 52,197 ms;remove 约 14,870 ms

Set 组快到夸张(finished before snapping fingers),List 组接近"整整一分钟"(almost a whole minute)。

结论(takeaway):在"查存在 + 删除"这类任务上,Set 明显更快(Sets dominate)。

13. 速度机制:为什么 Set 快( Mechanics of Speed

13.1 Set 优势( Set Advantage

    • Set 不用索引(don't use indexes)。
    • HashSet 通过哈希(hash / hash code)直接定位数据"应该在哪里"(computes exactly where the data lives)。
    • contains() 像"瞬移到房间"(teleporting to the exact room)。

核心:哈希查找接近 O(1)(near O(1) lookup)。

13.2 List 困境( List Struggle

    • List 强依赖索引/顺序(heavily indexed / sequential)。
    • 查找或删除往往要顺序检查(sequential check)或移动大量元素(shift massive amounts)。
    • 类比:走廊逐个开门(checking every single door)。

关键词:线性时间(O(n) linear)、移动元素(shifting)。

13.3 " 黄金法则 " Golden Rule

    • Set 在测试成员关系(membership)和删除无重复元素(removing non-duplicate elements)时,比 List"指数级更高效"(exponentially more efficient)。
      (严格算法角度通常说"数量级差异/大幅更高效",但图片用词是 exponentially)

14. 选型:选择你的 Set Choosing Your Set

根据任务需求(mission requirement)选择工具(weapon of choice):

    • 需求:存无序且不重复,追求最大效率(unordered, non-duplicate, maximum efficiency)
      → HashSet
    • 需求:不重复,但必须保留插入顺序(preserve exact insertion order)
      → LinkedHashSet
    • 需求:不重复,并且按递增/字母序排序(sorted increasing/alphabetical order)
      → TreeSet

15. 选型:选择你的 List Choosing Your List

强调:不是不用 List,而是在需要时用(we don't discard Lists)。

    • 需求:固定数量元素且允许重复(fixed number, duplicates allowed)
      → Array / ArrayList
    • 需求:经常在末尾追加/删除(frequent append/delete at end)
      → ArrayList
    • 需求:经常在开头插入/删除(frequent insert/delete at beginning)
      → LinkedList

16. 任务完成总结( Mission Accomplished

    • List 擅长精确索引访问(precise indexed access)。
    • 但当目标是"唯一性 + 快速查询"(uniqueness + rapid lookups),应信任 Set

战术速查表(Tactician's Cheat Sheet):

    • HashSet:纯速度 + 唯一(pure speed & uniqueness)
    • LinkedHashSet:唯一 + 插入顺序(uniqueness + insertion order)
    • TreeSet:唯一 + 排序顺序(uniqueness + sorted order)
    • ArrayList:索引 + 尾部修改(indexing & end-modifications)
    • LinkedList:头部修改(head-modifications)

17. 案例:战术关键词提取( Tactical Keyword Extraction

17.1 任务参数( Mission Parameters

    • 目标文件(Target):标准 Java 源文件(Java source file)。
    • 目标(Objective):扫描整个文件并统计保留字数量(scan & count reserved Java keywords)。
    • 约束(Constraint):高效率(high-efficiency execution);要把 53 个关键词与文件中可能成千上万词进行匹配(cross-reference 53 targets vs thousands of words)。
    • 强调:暴力法(brute force)不行,需要战术数据结构(tactical data structure)。

17.2 为什么选 HashSet Why HashSet?

三点理由:

    • O(1) 查找速度( O(1) lookup speed :用哈希码分散元素,实现近乎瞬时成员测试(near-instant membership testing)。
    • 不需要重复( no duplicates needed :Java keywords 唯一,Set 天然拒绝重复(reject duplicates)。
    • 无序没关系( unordered architecture :只关心是不是关键字,不关心存储顺序(don't care order)。
    • 关键词提取四阶段(Phases)

Phase 1 :定义目标( Defining the Targets

|---|------------------------------------------------------------|
| | |

    • 先把所有 Java 关键字 + 字面量(keywords + literals)硬编码进字符串数组(String\[\])。
    • 图片特别提到包含 "true", "false", "null":虽然是字面量(literals),但在语法扫描时像关键字一样处理(act like keywords)。

Phase 2 :武装数据( Weaponizing the Data

    • 一行核心构造:
      • Set<String> keywordSet = new HashSet<>(Arrays.asList(keywordString));
    • 步骤含义:
      • Arrays.asList() 把数组转成 List(Array → List)。
      • HashSet 构造器接收 Collection,并把 53 个词哈希进内存以便 O(1) 查询(hash into memory for O(1) retrieval)。
    • 图片吐槽:不必手写循环(manual loop),API 更优雅(elegance)。
    • 用 Scanner input = new Scanner(System.in); 读用户输入文件名(filename)。
    • File file = new File(filename); 创建文件对象(file object)。
    • if (file.exists()) { countKeywords(file); }
    • 提示:不要在没确认文件存在(exists check)前就扫描(never deploy without confirming target exists)。

Phase 4 :执行扫描( Executing the Sweep

    • 流程图:Scanner.next() 提取单词(extract word) → keywordSet.contains(word) O(1) 查询 → 若 true 则 count++。
    • 代码框架:
      • Scanner input = new Scanner(file); int count = 0;
      • while (input.hasNext()) { String word = input.next(); if (keywordSet.contains(word)) count++; }
      • return count;
    • 图片总结:Scanner 把每个词分离(isolated),contains 命中就自增(increment),干净高效(clean & efficient)。

19. 行动后报告:替代方案对比( After Action Report: Tactical Alternatives

表格比较:

    • LinkedHashSet:能用(Yes),查找快(Fast),但"没必要"(Unnecessary):我们不需要插入顺序(don't need insertion order)。
    • ArrayList :能用(Yes),但查找慢(Slow O(n)),因为对每个词都要扫描整个列表(scan whole list for every word)。
      结论:标准 HashSet 最优(optimal solution)。

20. 输出验证( Output Verification

    • 示例输出 1:输入 Welcome.java,输出关键字数量为 5。
    • 示例输出 2:输入 GhostFile.java,提示文件不存在(does not exist)。
    • 关键结论(Key takeaway):数据结构要对齐主要操作(align data structure with primary operation);对大规模频率/存在查询(frequency/existence queries),HashSet 是最佳(undisputed champion)。

21 战术数据管理: Java 的映射( Tactical Data Management: Mapping Intel in Java

    • 强调:原始数据(raw data)本身没用,关键是把情报映射到目标(map intel to targets),要高效(efficiently)且无重复(without duplication)。
    • 本节主题:用于快速检索(fast retrieval)的终极数据结构:Java Maps(Java Maps)。

22. Map 的核心概念( Core Concept of Maps

    • Map 是存储键值对(key/value pairs)的容器(container)。
    • key 类似 List 的索引(indices),但 Map 的 key 可以是任何对象(any object),不只是整数(not just integers)。
    • 关键规则(vital rule):Map 不能有重复键(no duplicate keys)。
    • 每个键映射到且只映射到一个值(each key maps to exactly one value)。
    • 一对 key+value 称为条目(entry)。
      • 图示:Search key(如社保号)→ Corresponding value(姓名)。

23. Map 接口:更新与查询( Map Interface: Updating & Querying

图中把 java.util.Map<K,V> 的常用方法分两类:

(1) 更新方法( Update Methods

    • clear():清除所有条目(removes all entries)。
    • put(key: K, value: V):添加/替换条目(adds/replaces an entry)。
    • putAll(m: Map):把 m 的所有条目加入(adds all entries from m)。
    • remove(key: Object):删除指定 key 的条目(removes entry for key)。

(2) 查询方法( Query Methods

    • containsKey(key: Object):若 key 存在返回 true(returns true if key exists)。
    • containsValue(value: Object):若 value 存在返回 true(returns true if value exists)。
    • isEmpty():是否空 map(checks if map has no entries)。
    • size():条目数量(returns number of entries)。

24. Map 接口:集合视图与迭代( Collection Views & Iteration

    • 重点:你不能直接遍历 Map(can't iterate over a Map directly)。
    • 必须请求某种"集合视图"(collection view):

(1) keySet() keys view

    • keySet():返回所有 key 的集合(returns a Set<K> of all keys)。

(2) values() values view

    • values():返回所有 value 的集合(returns a Collection<V> of all values)。

(3) entrySet() entries view

    • entrySet():返回所有条目(returns a Set<Map.Entry<K,V>>)。

(4) Java 8 forEach cleaner iteration

    • 图片给出更简洁遍历:
      map.forEach((k, v) -> System.out.println(k + ": " + v));
    • 关键词:forEach、lambda

25. 解剖节点: Map.Entry Dissecting the Node: Map.Entry

    • 每一对 key/value 是内部接口 java.util.Map.Entry<K,V> 的实例(instance)。
    • Map.Entry 关键方法(key methods):
      • getKey():返回 key(returns the key)。
      • getValue():返回 value(returns the value)。
      • setValue(value: V):替换 value(replaces the value)。
    • 战术注记(tactical note):AbstractMap 是便利抽象类(convenience abstract class),实现大多数 Map 方法(implements Map methods),但 entrySet() 例外(except entrySet)。

三大实现概览( The Big Three: Implementation Overview

Map 的三大常用实现(big three):

    • HashMap
    • LinkedHashMap
    • TreeMap

26. HashMap :原始速度( HashMap: Raw Speed and Efficiency

    • 适用:不关心顺序(don't care about order)。
    • 条目顺序随机/不可预测(order random/unpredictable)。
    • 最有效率(most efficient)用于定位/插入/删除(locating, inserting, deleting)。
    • 关键规格(key specs):
      • 默认容量(default capacity)16
      • 默认负载因子(load factor)0.75
      • 可自定义构造(custom constructor):HashMap(initialCapacity, loadFactor)

27. LinkedHashMap :维持顺序( LinkedHashMap: Maintaining Order

    • 基于 HashMap 并加链表结构(extends HashMap + linked-list implementation)。
    • 两种顺序模式(order modes):

(1) 插入顺序( Insertion order, default

    • 按添加顺序取出(retrieved in the exact order items were added)。

(2) 访问顺序( Access order

    • 按访问从"最久未访问 → 最近访问"(least recently accessed → most recently accessed)。
    • 构造器(constructor)示例:
      LinkedHashMap(initialCapacity, loadFactor, true)
      其中 true 表示 access order(访问顺序)。

28. TreeMap :排序情报( TreeMap: Sorted Intelligence

    • TreeMap 保证 key 完美排序(keys perfectly sorted)。
    • 插入更慢(takes more time to insert),但允许精确遍历(precise traversal)。
    • 排序机制(sorting mechanisms):
      • 默认:使用 compareTo(requires keys implement Comparable)。
      • 自定义:构造器 TreeMap(Comparator c) 定义排序逻辑(custom sorting logic)。
    • 关系说明(hierarchy note):TreeMap 实现 SortedMap 与 NavigableMap。

用"范围尺"(range number line)展示按目标 key(target key)定位:

    • lowerKey:严格小于(strictly less)
    • floorKey:小于等于(<=)
    • ceilingKey:大于等于(>=)
    • higherKey:严格大于(strictly greater)

SortedMap 方法(SortedMap methods):

    • firstKey() / lastKey():最小/最大 key(first/last)。
    • headMap(toKey) / tailMap(fromKey):返回 map 的一部分(returns portions)。

NavigableMap 额外方法(additional navigable methods):

    • pollFirstEntry():移除并返回第一个条目(removes and returns first)。
    • pollLastEntry():移除并返回最后一个条目(removes and returns last)。

30. 真实执行: Hash vs Tree Real-World Execution: Hash vs Tree

    • 示例:先建 HashMap 放入若干键值(Smith/Anderson/Lewis/Cook → ages)。
    • 输出(output)是随机顺序(random)。
    • 再 new TreeMap<>(hashMap):key 会立刻按字母升序排序(sorted ascending alphabetically)。
    • 图片强调:HashMap 输出是乱的;TreeMap 输出自动排序。

31. 真实执行:访问顺序( Real-World Execution: Access Order

    • 建 LinkedHashMap<>(16, 0.75f, true)(true=access order)。
    • 当调用 get("Lewis") 后,Lewis=29 被移动到输出的最后(moved to the very end),表示"最近访问"(most recently accessed)。
    • 这就是 access order 的效果(access-order behavior)。

32. 遗留情报: Hashtable Legacy Intel: Hashtable

    • 在现代集合框架(modern Collections Framework)之前,使用 java.util.Hashtable。
    • 它像 HashMap(works like HashMap),但更新方法是同步的(update methods synchronized)。
    • 关键结论(key takeaways):
      • 后来被重新设计以适配框架(redesigned)。
      • 保留仅为向后兼容(backwards compatibility)。
      • 建议(tactical advice):除非你确实需要老系统上的同步线程安全更新(synchronized thread-safe updates on legacy systems),否则用现代 Maps(modern Maps)。

33. 战术决策矩阵( Tactical Decision Matrix

问:更新 Map 时需要维护顺序吗(maintain order when updating the map)?

    • 不需要(No)→ HashMap:最快、无序(fastest, unordered, default)。
    • 需要插入顺序或访问历史(Yes, insertion or access history)→ LinkedHashMap:可追踪历史(tracks history),但内存成本略高(slightly higher memory cost)。
    • 需要按 key 排序(Yes, sorted by key)→ TreeMap:按 Comparable/Comparator 排序(sorted via Comparable/Comparator),支持分析式导航(analytical navigation)。

34. 文本词频案例:解析频率模式( Parsing Frequency Patterns

目标:把原始文本(raw, unstructured text)中的每个词计数(count occurrences),并按字母顺序输出(output alphabetically)。

    • 数组做会很乱(messy),需要高效结构(efficient data structure)。

(1) 选择装备用什么 Map Choosing the Right Loadout

对比表(table):

    • HashMap:插入/查找快 O(1),但无顺序(unpredictable/random)。
    • LinkedHashMap:插入/查找快 O(1),但顺序是插入或访问(insertion/access order)。
    • TreeMap:插入/查找 O(log n),但 key 升序(ascending key order)。
      图片结论:虽然 HashMap 最快,但任务需要按字母排序输出,所以 TreeMap最适合(perfect tool)。

(2) 数据破译:分词( Tokenization

    • 原始文本噪声大(noisy),不能只按空格切(split by spaces)。
    • 使用正则分隔符(regex delimiter):"\\\\s+\\\\p{P}"
      • 去掉空白(whitespaces ++++\\s++++)
      • 去掉标点(punctuation ++++\\p{P}++++)
    • 然后统一转小写(normalize to lowercase):toLowerCase()
      • 让 "Good" 与 "good" 同等(treated the same)。

代码示例:

    • String\[\] words = text.split("\\\\s+\\\\p{P}");

(3) 核心逻辑循环( Core Logic Loop

流程图逻辑:

    • 取 token:key = wordsi
    • 丢空串(drop token):如果 key.length() == 0
    • 判断是否已出现:map.containsKey(key)
    • 如果没有(No)→ map.put(key, 1)
    • 如果有(Yes)→ value = map.get(key);value++;map.put(key, value)

图片提示:put() 有"双重作用"(double duty):

    • key 不存在时建立初值(establish baseline count)
    • key 存在时覆盖旧值(overwrite old value)为递增结果。

35. 情报存储协议:包装类( Wrapper Classes

    • 词频统计用 Map<String, Integer> map = new TreeMap<>();:key 是单词(String),value 是次数(Integer)。
    • 关键点:Java Collections Framework 只能存对象(stores Objects),不能直接用基本类型 int(primitive int)作为 Map 的 value。
    • 所以用包装类(wrapper class)Integer 来装计数。
    • 当我们做 value++ 或 map.put(key, value+1) 时,Java 会自动装箱/拆箱(autoboxing/unboxing)。

36. 执行与输出( Execution & Output

    • 图片表示:数据已经映射好了(data mapped),不用"外部迭代器"(external iterator)那种笨重方式(clunky)。
    • 用 Java 8 的 forEach(forEach)+ lambda(lambda)输出:
      map.forEach((k, v) -> System.out.println(k + "\t" + v));
    • 输出面板示例(Output):
      • a 2
      • class 1
      • good 3
    • 注意:TreeMap 默认按 key 排序(sorted by keys, alphabetical),所以输出顺序是按字母(alphabetical)。

37. 改任务:要按频率排序怎么办( Changing Mission Parameters

    • TreeMap 能按 key 排序(sort by keys),但如果需求改成"最高频率优先"(most frequent first / sort by values),Map 本身做不到(a Map inherently cannot sort by its values)。
    • Map 只保证 key 唯一(uniqueness on keys),不保证 value 唯一(values can repeat)。
    • 所以必须把 Map 条目抽出来(extract entries),放到一个可以排序的结构里(structure that can be sorted)。

38. 战术数据抽取: entrySet → List Tactical Data Extraction

    • 抽取流程(pipeline):
      • map.entrySet() 得到 Set<Map.Entry<K,V>>(entrySet view)
      • new ArrayList<>(entrySet) 变成 List<Map.Entry<K,V>>(ready for sorting)
    • 解释:从 Map 的"刚性结构"(rigid structure)里把 key/value 对转换成标准 List 序列(standard list sequence),然后就能用自定义 Comparator(custom comparator)排序。

39. 执行按 value 排序( Executing the Value Sort

    • 用 Collections.sort(Collections.sort)对 entries 列表排序:
      Collections.sort(entries, (entry1, entry2) -> entry1.getValue().compareTo(entry2.getValue()));
    • 关键点:
      • 比较的是 value(compare values),不是 key(not keys)。
      • getValue() 返回 Integer(wrapper),可用 compareTo(Comparable.compareTo)。
    • 这会得到按频率升序(ascending by frequency)的结果;若要降序(descending),可以交换 entry1/entry2 或用 reversed()。

40. 任务复盘:为什么要用 Map Mission Debrief: Why Maps?

    • 如果不用 Map,需要并行数组(parallel arrays)或自定义对象列表(custom object list),并且每次更新前都要手动遍历查存在(manual iteration to check existence)。
    • Map 的优势(advantages):
      • 查找(lookups):O(1) 到 O(log n)(取决于 HashMap/TreeMap)→ 频率验证很快(instant frequency verification)。
      • 自动排序(automated sorting):TreeMap 把 key 排序交给数据结构(delegates sorting to the structure)。
      • 流畅 API(fluid API):forEach 与 entrySet 支持顺滑的数据转换(seamless transformation)。
    • 图片总结:Map 把"索引与计数的复杂度"(complexity of indexing and non-integer keys)抽象掉(abstract away)。

41. 战术数据封锁:不可变与只读( Immutable & Unmodifiable

主题:保护数据不被篡改(tampered),把数据"锁起来"(lock down)。

静态工具腰带( Static Utility Toolbelt

    • 用 java.util.Collections 的静态方法(static methods)给现有结构加"访问规则"(access rules),但不改变底层接口(without changing underlying interface)。
    • 它适用于 List/Set/Map(operates on standard Lists, Sets, Maps)。

42. 基线:空常量( Empty Constants

    • 当你要返回"空结果"(empty result)时,用空常量(empty constants)避免创建对象开销(avoid object creation overhead):
      • Collections.EMPTY_SET
      • Collections.EMPTY_LIST
      • Collections.EMPTY_MAP
    • 关键词:空对象模式(empty object pattern)、减少开销(reduce overhead)。

43. 单例集合:一次性集合( Singletons: One-shot Collections

    • 单例(singleton)= 只包含一个不可变元素(single, unchangeable item),内存高效(memory-efficient)。
    • 三类方法(arsenal matrix):
      • Collections.singleton(o) → 返回只含一个元素的不可变 Set(immutable Set)。
      • Collections.singletonList(o) → 不可变 List(immutable List)。
      • Collections.singletonMap(key, value) → 只含一个键值对的不可变 Map(immutable Map)。

修改单例会怎样( Modifying a Singleton

    • 示例:
      Set<String> set = Collections.singleton("Chicago");
      set.add("Dallas");
    • 结果:立刻抛 UnsupportedOperationException(breach detected)。
    • 结论:单例集合结构不可改(structural modifications forbidden)。

44. 不可修改视图:安全摄像头( Unmodifiable Views

    • unmodifiableList 不是拷贝(not a copy),而是"实时视图"(live feed)。
    • 外部程序能看(can watch),但不能改(cannot touch)。
    • 三个要点:
      • 原集合仍可在内部被修改(mutable collection can change internally)。
      • 视图与原集合动态连接(dynamic link),原集合变了视图立刻更新(updates instantly)。
      • 通过只读视图对外暴露更安全(safe exposure)。

(1) 不可修改包装器清单( Unmodifiable Wrapper Arsenal

    • unmodifiableCollection(c)
    • unmodifiableList(list)
    • unmodifiableMap(m)
    • unmodifiableSet(s)
    • unmodifiableSortedMap(sm)
    • unmodifiableSortedSet(ss)
      用途:给现有集合加"只读护盾"(read-only shield)。

(2) 尝试绕过视图( Bypassing the camera

    • 对只读视图调用 remove("Dallas") 仍会抛 UnsupportedOperationException(breach detected)。

45. 现代捷径: JDK 9+ 工厂方法( JDK 9+ Factory Methods

    • 老方法(old way):Collections.unmodifiableSet(new HashSet<>(Arrays.asList("A","B")))(需要 wrapper)。
    • 快方法(fast way):
      • Set.of("A","B")
      • Map.of("Key1","Val1","Key2","Val2")
    • 这些 of() 直接返回完全不可变集合(fully immutable collections),没有 wrapper 开销(without wrapper overhead)。

46. 选择你的 " 封锁策略 " Choosing your Lockdown Strategy

表格总结四种策略(strategy):

    • 空常量(Empty Constants)
    • 用途:返回安全的空对象(safe null-object patterns)
    • 底层可变?否(No)
    • 执行:Collections.EMPTY_LIST
    • 单例(Singletons)
    • 用途:只存一个元素,最省内存(exactly 1 item, minimal memory)
    • 底层可变?否(No)
    • 执行:Collections.singleton(o)
    • 不可修改视图(Unmodifiable Views)
    • 用途:安全对外暴露内部数据(exposing internal data safely)
    • 底层可变?是(Yes, by internal code)
    • 执行:Collections.unmodifiableList(list)
    • JDK 9 工厂(JDK 9 Factory)
    • 用途:快速硬编码固定数据集(rapidly hardcoding fixed datasets)
    • 底层可变?否(No)
    • 执行:Set.of(e1, e2)
    • 战术数据结构(Tactical Data Structures)

集合( Sets )与映射( Maps

目标:为高性能 Java(High-performance Java)选择合适框架(right framework)

1) 为什么不只用 ArrayList Why not just ArrayList

    • 学生疑问:去年什么都用 ArrayList(ArrayList for everything),为什么现在要学新集合(new collections)?
    • 回答要点:
      • 如果需要保证无重复(no duplicates guarantee)→ List 不擅长。
      • 如果需要用唯一 ID 作为键快速查详情(instant lookup by unique ID as a key)→ 需要 Map。
      • 做高性能操作(high-performance operations)要用专门装备(specialized loadout):Set 和 Map。

2) 关键概念( Key Concept

    • Set(Set):高效处理不重复元素(efficient for processing non-duplicate elements)。
    • Map(Map):字典结构(dictionary structure),用键(Key)快速取值(retrieve a Value quickly)。

3) Set " 会员俱乐部 " Set as exclusive club

    • Set 不允许重复(no duplicates allowed)。
    • 规则:如果两个对象 equals() 相等(objects are equals),Set 只保留一个(keeps one)。
    • 示例代码(HashSet):重复 add "B-Site"、"A-Site" 会被忽略(ignored),size 不变(size remains 2)。
    • 图示:输入流(Input stream)里重复元素被过滤(filtered),输出(Output)只有唯一项。

4) Set 武器库( The Set Arsenal

表格总结三种 Set:

4.1 HashSet HashSet

    • 顺序(Ordering):不可预测(unpredictable)。
    • 底层技术(Underlying tech):哈希表(hash table)。
    • 最适合(Best for):追求最大速度(maximum speed),不关心顺序(order doesn't matter)。

4.2 LinkedHashSet LinkedHashSet

    • 顺序:插入顺序(insertion order)。
    • 底层:链表 + 哈希(linked list + hash)。
    • 最适合:保持添加顺序(preserve exact order added)。

4.3 TreeSet TreeSet

    • 顺序:排序(sorted,基于自然顺序 Comparable 或自定义 Comparator)。
    • 底层:红黑树(red-black tree)。
    • 最适合:实时按字母/数值排序(alphabetical/numerical sorting on the fly)。

5) HashSet 为什么能快速判重( Hashing Engine

    • 合同(Contract):若 o1.equals(o2) 为 true,则 hashCode() 必须相同(MUST be the same)。
      • 提示:通常要同时覆写(override both)equals 和 hashCode。
    • 容量与负载因子(Capacity & Load Factor):
      • 默认容量(default capacity)16
      • 默认负载因子(load factor)0.75
      • 负载因子决定何时扩容(dictates when it resizes)。
    • 计算(The Math):当元素数超过 capacity * loadFactor(如 16 * 0.75 = 12)时,会动态扩容(dynamically doubles)到 32,以保持接近 O(1) 查找速度(maintain O(1) search speeds)。
    • TreeSet 会自动排序(automatically sorts)。
    • 还提供相对某个值的精确定位方法(precise methods relative to a value):

常用方法(methods):

    • first() / last():取最小/最大元素(absolute extremes)。
    • headSet(E toElement):取目标之前所有元素(everything before)。
    • tailSet(E fromElement):取目标及之后所有元素(everything after including)。
    • lower(E) / higher(E):找严格小于/严格大于目标的最近元素(closest strictly less/greater)。
    • 注:排序依赖 Comparable(Comparable)或构造器传入 Comparator(custom Comparator)。

7) 性能对决( Performance Showdown, N=50,000

    • 测试:50,000 元素的成员测试(membership test)contains()。
    • Set 组耗时(ms):
      • HashSet 约 20 ms
      • LinkedHashSet 约 27 ms
      • TreeSet 约 47 ms
    • List 组耗时(ms):
      • ArrayList 约 39,802 ms
      • LinkedList 约 52,197 ms
    • 结论(Takeaway):
      • Set 用哈希/树实现近乎瞬时查找(near-instant lookups)。
      • List 必须逐个扫描(scan element-by-element),时间复杂度 O(N)。

8) Map 的解剖( Anatomy of a Map

    • 当你不仅想知道"存不存在"(exists),还想取"附带情报/资料"(intel attached),就用 Map。
    • Keys(keys):唯一集合(unique set),不允许重复(no duplicates allowed),可为任何对象(any object type)。
    • Values(values):与 key 关联的数据(data attached to key),可以重复(can be duplicated)。
    • Entry(entry):key/value 合体(combined pair),由 Map.Entry<K,V> 表示。

9) Map 武器库( The Map Arsenal

三种 Map:

9.1 HashMap HashMap

    • 顺序:随机(random)。
    • 最适合:插入/删除/定位值的纯速度(pure speed for inserting, deleting, locating values)。

9.2 LinkedHashMap LinkedHashMap

    • 顺序:插入顺序或访问顺序(insertion OR access order)。
    • 最适合:做 LRU 缓存(LRU, least recently used)或时间线(chronologies)。

9.3 TreeMap TreeMap

    • 顺序:按 key 排序(sorted by key)。
    • 最适合:字母目录(alphabetical directories)或按 key 升序遍历(iterating keys ascending)。

10) Map 交互武器库( Map Interaction Arsenal

10.1 更新方法( Update methods

    • put(K key, V value):插入条目(inserts entry);如果 key 已存在会覆盖旧值(overrides old value)。
    • remove(Object key):删除 key 对应条目(deletes entry associated with key)。

10.2 查询方法( Query methods

    • get(Object key):取 value(retrieves value);key 不存在返回 null(returns null)。
    • containsKey / containsValue:快速布尔检查(fast boolean checks)。

10.3 遍历 Map 的方式( How to iterate

    • 不能直接遍历 Map(cannot iterate directly)。
    • 必须提取本质(extract essence):
      • keySet() → 所有 keys 的 Set(Set of keys)
      • values() → 所有 values 的 Collection(Collection of values)
      • entrySet() → 所有 entry 的 Set(Set of Map.Entry)

10.4 Java 8+ 现代写法( Modern way

    • agents.forEach((name, level) -> System.out.println(name + " is level " + level));
    • 关键词:forEach(forEach)、lambda(lambda)。

11) 高级工具( Advanced Utilities

    • 只读视图(Read-only views):
      • Collections.unmodifiableSet(set) / Collections.unmodifiableMap(map)
      • 返回引用(reference),若尝试修改会抛 UnsupportedOperationException。
    • 单例集合(Singletons):
      • Collections.singleton(Object) 或 Collections.singletonMap(Key, Value)
      • 创建高度优化且不可变(immutable)的"一项集合"(exactly one item)。
    • 备注:JDK 9+ 可以用 Set.of() / Map.of() 快速创建不可变集合(instant immutable structures)。

12) 战术决策树( Tactical Decision Tree

    • 第一步:你需要键值对吗(need key-value pairs)?
      • Yes → Map
        • 需要 key 排序(need keys sorted)→ TreeMap
        • 需要插入/访问顺序(need insertion/access order)→ LinkedHashMap
        • 只要速度(just need pure speed)→ HashMap
      • No → 看是否允许重复(are duplicates allowed)?
        • Yes → List(ArrayList / LinkedList)
        • No → Set
          • 需要排序(need elements sorted)→ TreeSet
          • 需要插入顺序(need insertion order)→ LinkedHashSet
          • 只要速度(just need pure speed)→ HashSet

TLDR: Sets and Maps

1. 核心概念: Set vs Map Core Concepts: Set vs Map

  • 集合(Set)存储无序且不重复元素(unordered, nonduplicate elements)。
  • 映射(Map)存储键/值对(key/value pairs),并支持按键快速查找(fast lookup by key)。
  • Set 继承 Collection(Set extends Collection);Map 是独立层级(separate hierarchy)。
  • List 的索引(index)是整数(integers);Map 的键(key)可以是对象(objects)。
  • Map 不能有重复键(no duplicate keys);一个键最多映射一个值(one key maps to at most one value)。
  • AbstractSet / AbstractMap 是便利抽象类(convenience abstract classes),提供部分实现(partial implementations)。

2. Set 接口与常用操作( Set Interface and Common Operations

  • Set 不新增方法(adds no new methods beyond Collection),核心规则是不重复(no duplicates)。
  • add(e):若元素不存在才插入(inserts if not already present)。
  • remove(o):删除元素(removes an element)。
  • contains(o):成员测试(membership test)。
  • size():元素数量(number of elements)。
  • isEmpty():是否为空(has no elements)。
  • clear():清空(removes all elements)。
  • 批量操作(bulk updates):
    • addAll(c):并集式更新(union-style)
    • removeAll(c):差集式更新(difference-style)
    • retainAll(c):交集式更新(intersection-style)
  • forEach(action):对每个元素执行操作(process each element)。

3. HashSet / LinkedHashSet / TreeSet Set Implementations

  • HashSet:不保证顺序(no ordering);只需要"快速+不重复"(fast duplicate-free storage)时最佳。
  • LinkedHashSet:保持插入顺序(keeps insertion order)。
  • TreeSet:保持排序(keeps elements sorted),依赖 Comparable 或 Comparator(Comparable or Comparator)。

常见构造(constructors):

  • new HashSet<>():默认空集合(default empty set)
  • new HashSet<>(collection):从已有集合构建(build from existing collection)
  • new HashSet<>(initialCapacity, loadFactor):已知规模时调参(tune storage)
  • new LinkedHashSet<>():插入有序(insertion-ordered)
  • new TreeSet<>():自然顺序排序(natural order)
  • new TreeSet<>(collection):把无序数据变成有序副本(sorted copy)
  • new TreeSet<>(comparator):自定义排序(custom order)

TreeSet 相关方法(SortedSet/NavigableSet methods):

  • first()/last():最小/最大元素(smallest/largest)
  • headSet(to)/tailSet(from):有序子集视图(sorted subsets)
  • lower/higher/floor/ceiling:邻近元素(nearest neighbors)
  • pollFirst()/pollLast():移除并返回两端元素(remove and return ends)

4. 性能: Set vs List Performance: Sets vs Lists

  • 文档指出:在成员测试和删除(membership testing and removal)实验里,Set 比 List 高效得多(much more efficient)。
  • List 仍然有用:当你需要基于索引访问(index-based access)时。
  • 经验:无序且不重复(unordered, nonduplicate)→ 用 Set;需要重复或索引(duplicates or index)→ 用 List。
  • "关键字是否存在"这类任务(keyword lookup / "is item present?")优先选 HashSet。

5. Map 接口与核心方法( Map Interface and Core Methods

  • Map 存储 entry(entries)= key/value 对(key/value)。
  • 核心工作(core jobs):查询(query)、更新(update)、取 keys/values/entries(retrieve views)。

常用方法(core methods):

  • put(key, value):添加或更新(adds or updates)
  • get(key):取值(retrieves value)
  • containsKey(key):是否有键(key exists)
  • containsValue(value):是否有值(value exists)
  • remove(key):删除条目(removes entry)
  • size()/isEmpty()/clear():数量/空/清空(count/empty/clear)
  • putAll(m):复制另一个 map 的所有条目(copies all entries)

集合视图(views):

  • keySet() → Set<K>
  • values() → Collection<V>
  • entrySet() → Set<Map.Entry<K,V>>
  • forEach((k,v)->...):遍历 key/value(process each pair)

6. HashMap / LinkedHashMap / TreeMap Map Implementations

  • HashMap:查找/插入/删除高效(efficient for lookup, insertion, deletion),不保证顺序

(no order)

  • LinkedHashMap :保持插入顺序或访问顺序(insertion order or access order)。
    • new LinkedHashMap<>(16, 0.75f, true):true 表示访问顺序(access-ordered)。
  • TreeMap :按 key 排序(entries sorted by key)。
    • 可用自然顺序(natural order)或比较器(comparator)排序。

TreeMap 相关方法(SortedMap/NavigableMap):

  • firstKey()/lastKey():最小/最大键(smallest/largest key)
  • headMap(to)/tailMap(from):范围视图(range views)
  • headMap(toKey):左半边 (< toKey)
  • tailMap(fromKey):右半边 (>= fromKey)
  • lowerKey/higherKey/floorKey/ceilingKey:邻近键(nearest keys)
  • pollFirstEntry()/pollLastEntry():移除并返回两端 entry(remove & return ends)

7. 遍历 Map 的方法( Traversing Maps

  • 遍历途径:keySet(keys)、values(values)、entrySet(entries)、forEach(lambda)。
  • Map.Entry 常用:getKey()/getValue() 读取;setValue(value) 通过 entry 更新。
    • lowerKey("M"):返回 严格小于 "M" 的最大 key
      �� "M 左边最近的一个"(<)
    • floorKey("M"):返回 小于等于 "M" 的最大 key
      �� "M 左边最近,若刚好有 M 就返回 M"(<=)
    • ceilingKey("M"):返回 大于等于 "M" 的最小 key
      �� "M 右边最近,若刚好有 M 就返回 M"(>=)
    • higherKey("M"):返回 严格大于 "M" 的最小 key
      �� "M 右边最近的一个"(>)

8. 常见高分用法( Common Use Patterns

  • HashSet 做快速成员测试(fast membership test)------关键字查找(keyword lookup)。
  • TreeMap 做计数并按 key 排序输出(count occurrences with sorted output by key)。
  • 需要排序时再转换(convert unsorted → sorted only when needed):
    • new TreeSet<>(set),new TreeMap<>(map)。
  • 保持插入顺序(preserve insertion order)用 LinkedHashSet/LinkedHashMap。

9. 不可变与只读( Immutable and Unmodifiable Collections

  • 单例集合/映射(singleton collections/maps)是不可变(immutable)。
  • unmodifiable... 返回只读视图(read-only views),修改会抛 UnsupportedOperationException。
  • 例子:
    • Collections.singleton(o) / singletonList(o) / singletonMap(k,v)
    • Collections.unmodifiableSet/ List/ Map
    • Collections.unmodifiableSortedSet/ unmodifiableSortedMap
  • JDK 9:Set.of(...) / Map.of(...) 创建不可变集合(immutable set/map)。

10. 选型总结( Choosing the Right Data Structure

  • HashSet:快速、无重复、无顺序需求(fast duplicate-free membership, order doesn't matter)。
  • LinkedHashSet:无重复 + 插入顺序(duplicate-free + insertion order)。
  • TreeSet:无重复 + 排序(duplicate-free + sorted)。
  • HashMap:按 key 快查,顺序不重要(fast key lookup, no order)。
  • LinkedHashMap:插入/访问顺序(insertion order or access order)。
  • TreeMap:按 key 排序(sorted by key)。
  • 需要重复或索引(duplicates or index access)→ List。

11. 常见考试陷阱( Common Exam Traps and Quick Reminders

  • Set 不允许重复;List 允许重复(duplicates)。
  • Map 不允许重复 key;同 key 的后一次 put 会替换旧值(replaces old value)。
  • HashSet/HashMap 不保证插入顺序(do not preserve insertion order)。
  • LinkedHashSet 保留插入顺序;LinkedHashMap 可保留插入或访问顺序。
  • TreeSet/TreeMap 需要元素/键可比较(Comparable),或提供 Comparator。
  • TreeSet 中比较结果为 0 会被当作重复(comparison returns 0 ⇒ duplicates)。
  • HashSet 的重复处理依赖 equals 与一致的 hashCode(consistent hashing)。
  • Set 不支持下标(no indexing);用 foreach 遍历。
  • 泛型要用包装类型(wrapper types):Map<String,Integer> ✅,Map<String,int> ❌。
  • unmodifiable... 是只读视图(view),不是深拷贝(deep copy)。
  • singleton 与 Set.of/Map.of 是不可变(immutable)。

lab sheet

0) 实验目标与总体要求( Lab goals & overall requirements

  • 本实验用于在 Week 5 的 todo 系统基础上扩展(extend Week 5 system),引入:
    • Set:处理不重复数据(nonduplicate data)与成员测试(membership testing)
    • Map:键值关系(key-value relationships)与快速查找(fast lookup)
    • TreeSet / TreeMap(TreeSet & TreeMap):有序数据表示(sorted data representation)
  • 必须继续在同一个 todo 管理系统上开发(same todo management system),不要新建程序(do not create a new program)。每个任务(task)都是在原系统上叠加新能力(adds new capabilities)。

Task 1 :用 Set 做任务标签系统( Task Tagging System with Set

场景( Scenario

  • 任务通常会用标签(tags)分类,如 study / urgent / email。一个任务不应该有重复标签(no duplicate tags)。

必做功能( Required features

  • 给任务添加标签(add tags to a task)。
  • 阻止重复标签(prevent duplicate tags)。
  • 显示每个任务的标签(display tags for each task)。
  • 检查标签是否存在(check tag membership)。

实现约束( Implementation constraints

  • 使用集合:Set tags = new HashSet<>();(HashSet)

Task 2 :全局标签管理与过滤( Global Tag Management and Filtering

场景( Scenario

  • 用户想查看所有标签(explore all tags)并按标签过滤任务(filter tasks)。

必做功能( Required features

  • 收集全部唯一标签(collect all unique tags)。
  • 显示所有标签(display all tags)。
  • 按标签过滤任务(filter tasks by tag)。
  • 处理空结果(handle empty results)。

实现约束( Implementation constraints

  • 使用集合:Set allTags = new HashSet<>();(HashSet)

Task 3 :用 Map 做任务查找系统( Task Lookup System with Map

场景( Scenario

  • 基于 List 的查找效率低(list-based lookup is inefficient)。

必做功能( Required features

  • 给每个任务分配唯一 ID(assign unique ID)。
  • 用 Map 存任务(store tasks in a map)。
  • 通过 ID 检索任务(retrieve by ID)。
  • 保持一致性(maintain consistency)------说明你的主列表与 map 的数据需要一致更新。

实现约束( Implementation constraints

  • 使用:Map<Integer, Task> taskMap = new HashMap<>();(HashMap)

Task 4 :用 Map 做标签频率分析( Tag Frequency Analysis with Map

场景( Scenario

  • 分析标签出现频率(analyse how often tags appear)。

必做功能( Required features

  • 统计标签出现次数(count tag occurrences)。
  • 显示频率表(display frequencies)。
  • 找出出现最多的标签(find most frequent tag)。
  • 处理空情况(handle empty cases)。

实现约束( Implementation constraints

  • 使用:Map<String, Integer> tagFrequency = new HashMap<>();(HashMap)

Task 5 :用 TreeSet / TreeMap 输出有序洞察( Sorted Insights with TreeSet and TreeMap

场景( Scenario

  • 需要"有序输出"(ordered outputs)。

必做功能( Required features

  • 显示排序后的标签(display sorted tags)。
  • 显示排序后的频率(display sorted frequencies)。
  • 比较结构(compare structures)------对比 HashSet/HashMap 与 TreeSet/TreeMap 在"顺序/性能/用途"上的差别。
  • 可选:高级排序(optional advanced sorting)------如按频率、按字母序等组合规则。

实现约束( Implementation constraints

  • 使用 TreeSet(TreeSet)和 TreeMap(TreeMap<String, Integer>)。

总结( Summary

  • 你应理解:
    • Set 用于唯一性(uniqueness)
    • Map 用于查找(lookup)
    • Tree 结构用于有序(ordering)
  • 最终系统整合 Week 5 + Week 6 概念(integrates Week 5 and Week 6 concepts)。

TodoSystemWeek6Complete.java

import java.util.ArrayList;

import java.util.Comparator;

import java.util.HashMap;

import java.util.HashSet;

import java.util.LinkedList;

import java.util.List;

import java.util.Map;

import java.util.PriorityQueue;

import java.util.Queue;

import java.util.Scanner;

import java.util.Set;

import java.util.Stack;

import java.util.TreeMap;

import java.util.TreeSet;

/**

* Week 6 Complete Todo System (built on Week 5)

* Adds:

* - Per-task tags using Set<String> (no duplicates)

* - Global unique tag set (HashSet)

* - ID lookup map: Map<Integer, Task> (HashMap)

* - Tag frequency map: Map<String, Integer> (HashMap), plus Tree views (TreeSet/TreeMap)

*/

public class TodoSystemWeek6Complete {

private final Scanner input = new Scanner(System.in);

// Week 5: core structures

private final List<Task> tasks = new ArrayList<>();

private final Queue<Task> processingQueue = new LinkedList<>();

private final Stack<ActionRecord> recentActions = new Stack<>();

private final PriorityQueue<Task> priorityQueue = createPriorityQueue();

// Week 6: new structures

private final Set<String> allTags = new HashSet<>(); // all unique tags (unordered)

private final Map<Integer, Task> taskMap = new HashMap<>(); // ID -> Task lookup

private final Map<String, Integer> tagFrequency = new HashMap<>(); // tag -> count

private int nextTaskId = 1;

public static void main(String\[\] args) {

TodoSystemWeek6Complete system = new TodoSystemWeek6Complete();

system.run();

}

public void run() {

boolean running = true;

while (running) {

printMainMenu();

int choice = readInt("Choose an option: ");

switch (choice) {

case 1 -> addTask();

case 2 -> viewAllTasks();

case 3 -> editTask();

case 4 -> removeTask();

case 5 -> markTaskCompleted();

case 6 -> viewTasksByStatus();

case 7 -> moveTaskToQueue();

case 8 -> viewProcessingQueue();

case 9 -> viewNextQueueTask();

case 10 -> processNextQueueTask();

case 11 -> viewMostRecentAction();

case 12 -> removeMostRecentActionFromHistory();

case 13 -> viewRecentActionHistory();

case 14 -> assignPriorityToTask();

case 15 -> addTaskToPriorityQueue();

case 16 -> viewPriorityQueue();

case 17 -> viewHighestPriorityTask();

case 18 -> processHighestPriorityTask();

// Week 6

case 19 -> addTagToTask();

case 20 -> checkTaskTagMembership();

case 21 -> viewAllTags();

case 22 -> filterTasksByTag();

case 23 -> lookupTaskById();

case 24 -> viewTagFrequency();

case 25 -> viewMostFrequentTag();

case 26 -> viewSortedTags();

case 27 -> viewSortedTagFrequencies();

case 28 -> compareTagViews();

case 29 -> viewSystemOverview();

case 0 -> {

running = false;

System.out.println("Exiting Todo System.");

}

default -> System.out.println("Invalid option. Please try again.");

}

}

}

private void printMainMenu() {

System.out.println();

System.out.println("=== Todo System ===");

System.out.println("Week 5 - Core List Features");

System.out.println("1. Add task");

System.out.println("2. View all tasks");

System.out.println("3. Edit task");

System.out.println("4. Remove task");

System.out.println("5. Mark task as completed");

System.out.println("6. View tasks by status");

System.out.println();

System.out.println("Week 5 - Queue Features");

System.out.println("7. Move task to processing queue");

System.out.println("8. View processing queue");

System.out.println("9. View next queue task");

System.out.println("10. Process next queue task");

System.out.println();

System.out.println("Week 5 - Stack Features");

System.out.println("11. View most recent action");

System.out.println("12. Remove most recent action from history");

System.out.println("13. View recent action history");

System.out.println();

System.out.println("Week 5 - Priority Queue Features");

System.out.println("14. Assign priority to a task");

System.out.println("15. Add task to priority queue");

System.out.println("16. View priority queue");

System.out.println("17. View highest-priority task");

System.out.println("18. Process highest-priority task");

System.out.println();

System.out.println("Week 6 - Set and Map Features");

System.out.println("19. Add tag to a task");

System.out.println("20. Check task tag membership");

System.out.println("21. View all unique tags");

System.out.println("22. Filter tasks by tag");

System.out.println("23. Look up task by ID");

System.out.println("24. View tag frequencies");

System.out.println("25. View most frequent tag");

System.out.println("26. View sorted tags");

System.out.println("27. View sorted tag frequencies");

System.out.println("28. Compare hash-based and tree-based views");

System.out.println("29. View system overview");

System.out.println("0. Exit");

}

// ------------------------------------------------------------

// Week 5 core functionality (kept complete)

// ------------------------------------------------------------

private void addTask() {

String title = readNonEmptyLine("Enter task title: ");

Task task = new Task(nextTaskId, title);

nextTaskId++;

tasks.add(task);

// Keep map consistent (ID lookup)

taskMap.put(task.getId(), task);

recordAction(ActionType.ADD, task, "Added a new task.");

System.out.println("Task added.");

displayTask(task, tasks.size() - 1);

}

private void viewAllTasks() {

System.out.println("=== All Tasks ===");

if (tasks.isEmpty()) {

System.out.println("No tasks available.");

return;

}

int index = 0;

for (Task task : tasks) {

displayTask(task, index);

index++;

}

}

private void editTask() {

int index = chooseTaskIndex();

if (index == -1) return;

Task task = tasks.get(index);

String oldTitle = task.getTitle();

String newTitle = readNonEmptyLine("Enter the new title: ");

task.setTitle(newTitle);

// If task is in priorityQueue, reinsert to fix heap after title change

refreshPriorityQueueEntry(task);

recordAction(ActionType.EDIT, task, "Changed title from '" + oldTitle + "' to '" + newTitle + "'.");

System.out.println("Task updated.");

displayTask(task, index);

}

private void removeTask() {

int index = chooseTaskIndex();

if (index == -1) return;

Task task = tasks.remove(index);

boolean removedFromQueue = processingQueue.remove(task);

boolean removedFromPriorityQueue = priorityQueue.remove(task);

// Remove ID mapping and rebuild tag summaries

taskMap.remove(task.getId());

refreshTagData();

String details = "Removed task from main list.";

if (removedFromQueue) details += " Also removed from processing queue.";

if (removedFromPriorityQueue) details += " Also removed from priority queue.";

recordAction(ActionType.REMOVE, task, details);

System.out.println("Task removed.");

}

private void markTaskCompleted() {

int index = chooseTaskIndex();

if (index == -1) return;

Task task = tasks.get(index);

task.setCompleted(true);

recordAction(ActionType.COMPLETE, task, "Marked task as completed.");

System.out.println("Task marked as completed.");

displayTask(task, index);

}

private void viewTasksByStatus() {

System.out.println("1. View completed tasks");

System.out.println("2. View incomplete tasks");

int choice = readInt("Choose an option: ");

boolean targetCompleted;

if (choice == 1) targetCompleted = true;

else if (choice == 2) targetCompleted = false;

else {

System.out.println("Invalid option.");

return;

}

boolean found = false;

System.out.println(targetCompleted ? "=== Completed Tasks ===" : "=== Incomplete Tasks ===");

for (int i = 0; i < tasks.size(); i++) {

Task task = tasks.get(i);

if (task.isCompleted() == targetCompleted) {

displayTask(task, i);

found = true;

}

}

if (!found) System.out.println("No matching tasks found.");

}

private void moveTaskToQueue() {

int index = chooseTaskIndex();

if (index == -1) return;

Task task = tasks.get(index);

if (processingQueue.contains(task)) {

System.out.println("That task is already in the processing queue.");

return;

}

processingQueue.offer(task);

recordAction(ActionType.MOVE_TO_QUEUE, task, "Moved task into FIFO processing queue.");

System.out.println("Task added to processing queue.");

}

private void viewProcessingQueue() {

System.out.println("=== Processing Queue ===");

if (processingQueue.isEmpty()) {

System.out.println("The processing queue is empty.");

return;

}

int position = 1;

for (Task task : processingQueue) {

System.out.print(position + ". ");

displayTask(task);

position++;

}

}

private void viewNextQueueTask() {

Task task = processingQueue.peek();

if (task == null) {

System.out.println("The processing queue is empty.");

return;

}

System.out.println("Next queue task:");

displayTask(task);

}

private void processNextQueueTask() {

Task task = processingQueue.poll();

if (task == null) {

System.out.println("The processing queue is empty.");

return;

}

recordAction(ActionType.PROCESS_QUEUE, task, "Processed the next task from FIFO queue.");

System.out.println("Processed task:");

displayTask(task);

}

private void viewMostRecentAction() {

if (recentActions.isEmpty()) {

System.out.println("No recent actions recorded.");

return;

}

System.out.println("Most recent action:");

System.out.println(recentActions.peek());

}

private void removeMostRecentActionFromHistory() {

if (recentActions.isEmpty()) {

System.out.println("No recent actions to remove.");

return;

}

ActionRecord removed = recentActions.pop();

System.out.println("Removed action from history:");

System.out.println(removed);

}

private void viewRecentActionHistory() {

System.out.println("=== Recent Action History ===");

if (recentActions.isEmpty()) {

System.out.println("No recent actions recorded.");

return;

}

for (int i = recentActions.size() - 1; i >= 0; i--) {

System.out.println(recentActions.get(i));

}

}

private void recordAction(ActionType actionType, Task task, String details) {

recentActions.push(new ActionRecord(actionType, task, details));

}

private void assignPriorityToTask() {

int index = chooseTaskIndex();

if (index == -1) return;

Task task = tasks.get(index);

PriorityLevel oldPriority = task.getPriority();

PriorityLevel newPriority = choosePriorityLevel();

task.setPriority(newPriority);

// Reinsert if needed

refreshPriorityQueueEntry(task);

recordAction(ActionType.ASSIGN_PRIORITY, task,

"Changed priority from " + oldPriority + " to " + newPriority + ".");

System.out.println("Priority updated.");

displayTask(task, index);

}

private void addTaskToPriorityQueue() {

int index = chooseTaskIndex();

if (index == -1) return;

Task task = tasks.get(index);

if (priorityQueue.contains(task)) {

System.out.println("That task is already in the priority queue.");

return;

}

priorityQueue.offer(task);

recordAction(ActionType.ADD_TO_PRIORITY_QUEUE, task, "Added task to priority queue.");

System.out.println("Task added to priority queue.");

}

private void viewPriorityQueue() {

System.out.println("=== Priority Queue ===");

if (priorityQueue.isEmpty()) {

System.out.println("The priority queue is empty.");

return;

}

// Copy + sort for display (PriorityQueue iterator isn't sorted)

List<Task> orderedView = new ArrayList<>(priorityQueue);

orderedView.sort(

Comparator.comparingInt(Task::getPriorityValue).reversed()

.thenComparing(Task::getTitle, String.CASE_INSENSITIVE_ORDER));

int position = 1;

for (Task task : orderedView) {

System.out.print(position + ". ");

displayTask(task);

position++;

}

}

private void viewHighestPriorityTask() {

Task task = priorityQueue.peek();

if (task == null) {

System.out.println("The priority queue is empty.");

return;

}

System.out.println("Highest-priority task:");

displayTask(task);

}

private void processHighestPriorityTask() {

Task task = priorityQueue.poll();

if (task == null) {

System.out.println("The priority queue is empty.");

return;

}

recordAction(ActionType.PROCESS_PRIORITY_QUEUE, task, "Processed the highest-priority task.");

System.out.println("Processed highest-priority task:");

displayTask(task);

}

// ------------------------------------------------------------

// Week 6 features (Set / Map)

// ------------------------------------------------------------

// Task 1 (Week6): add tag to task (uses Set, rejects duplicates)

private void addTagToTask() {

int index = chooseTaskIndex();

if (index == -1) return;

Task task = tasks.get(index);

String tag = readTag("Enter a tag: ");

boolean added = task.addTag(tag); // HashSet.add returns false if duplicate

if (!added) {

System.out.println("Tag already exists for this task (duplicate rejected).");

displayTask(task, index);

return;

}

refreshTagData();

recordAction(ActionType.ADD_TAG, task, "Added tag '" + tag + "' to task.");

System.out.println("Tag added.");

displayTask(task, index);

}

// Task 1 (Week6): membership test in task's tag Set

private void checkTaskTagMembership() {

int index = chooseTaskIndex();

if (index == -1) return;

Task task = tasks.get(index);

String tag = readTag("Enter a tag to check: ");

boolean has = task.hasTag(tag);

System.out.println(has ? "YES: task contains tag '" + tag + "'." : "NO: task does not contain tag '" + tag + "'.");

displayTask(task, index);

}

// Task 2 (Week6): show all unique tags (HashSet, not sorted)

private void viewAllTags() {

System.out.println("=== All Unique Tags ===");

refreshTagData();

if (allTags.isEmpty()) {

System.out.println("No tags available.");

return;

}

for (String tag : allTags) {

System.out.println(tag);

}

}

// Task 2 (Week6): filter tasks by a tag

private void filterTasksByTag() {

refreshTagData();

if (allTags.isEmpty()) {

System.out.println("No tags available.");

return;

}

String tag = readTag("Enter a tag to filter by: ");

boolean found = false;

System.out.println("=== Tasks with tag '" + tag + "' ===");

for (int i = 0; i < tasks.size(); i++) {

Task task = tasks.get(i);

if (task.hasTag(tag)) {

displayTask(task, i);

found = true;

}

}

if (!found) System.out.println("No tasks found with that tag.");

}

// Task 3 (Week6): Map lookup by ID

private void lookupTaskById() {

if (taskMap.isEmpty()) {

System.out.println("No tasks available.");

return;

}

int id = readInt("Enter task ID: ");

Task task = taskMap.get(id);

if (task == null) {

System.out.println("No task found with ID " + id + ".");

return;

}

System.out.println("Found task:");

displayTask(task);

}

// Task 4 (Week6): show tag frequency (HashMap, unordered)

private void viewTagFrequency() {

System.out.println("=== Tag Frequency ===");

refreshTagData();

if (tagFrequency.isEmpty()) {

System.out.println("No tags available.");

return;

}

for (Map.Entry<String, Integer> entry : tagFrequency.entrySet()) {

System.out.println(entry.getKey() + " = " + entry.getValue());

}

}

// Task 4 (Week6): find most frequent tag

private void viewMostFrequentTag() {

refreshTagData();

if (tagFrequency.isEmpty()) {

System.out.println("No tags available.");

return;

}

String bestTag = null;

int bestCount = -1;

// Stable tie-break: alphabetical

for (Map.Entry<String, Integer> e : tagFrequency.entrySet()) {

String tag = e.getKey();

int count = e.getValue();

if (count > bestCount) {

bestCount = count;

bestTag = tag;

} else if (count == bestCount && bestTag != null && tag.compareTo(bestTag) < 0) {

bestTag = tag;

}

}

System.out.println("Most frequent tag: " + bestTag + " (" + bestCount + ")");

}

// Task 5 (Week6): TreeSet sorted tags

private void viewSortedTags() {

refreshTagData();

System.out.println("=== Sorted Tags (TreeSet) ===");

if (allTags.isEmpty()) {

System.out.println("No tags available.");

return;

}

TreeSet<String> sorted = new TreeSet<>(allTags);

for (String tag : sorted) {

System.out.println(tag);

}

}

// Task 5 (Week6): TreeMap sorted frequencies by key (tag)

private void viewSortedTagFrequencies() {

refreshTagData();

System.out.println("=== Sorted Tag Frequency (TreeMap) ===");

if (tagFrequency.isEmpty()) {

System.out.println("No tags available.");

return;

}

TreeMap<String, Integer> sorted = new TreeMap<>(tagFrequency);

for (Map.Entry<String, Integer> e : sorted.entrySet()) {

System.out.println(e.getKey() + " = " + e.getValue());

}

}

// Task 5 (Week6): compare hash-based vs tree-based views

private void compareTagViews() {

refreshTagData();

System.out.println("=== Hash vs Tree Comparison ===");

if (allTags.isEmpty()) {

System.out.println("No tags available.");

return;

}

System.out.println("HashSet allTags (unordered): " + allTags);

System.out.println("TreeSet sortedTags (sorted): " + new TreeSet<>(allTags));

System.out.println("HashMap tagFrequency (unordered): " + tagFrequency);

System.out.println("TreeMap sortedTagFrequency (sorted by key): " + new TreeMap<>(tagFrequency));

System.out.println();

System.out.println("Explanation:");

System.out.println("- HashSet/HashMap do NOT guarantee iteration order (hash-based).");

System.out.println("- TreeSet/TreeMap keep keys/elements sorted (tree-based).");

}

private void viewSystemOverview() {

refreshTagData();

System.out.println("=== System Overview ===");

System.out.println("Task count: " + tasks.size());

System.out.println("Task map size: " + taskMap.size());

System.out.println("Queue size: " + processingQueue.size());

System.out.println("Recent action count: " + recentActions.size());

System.out.println("Priority queue size: " + priorityQueue.size());

System.out.println("Unique tag count: " + allTags.size());

int completedCount = 0;

for (Task task : tasks) {

if (task.isCompleted()) completedCount++;

}

System.out.println("Completed tasks: " + completedCount);

System.out.println("Incomplete tasks: " + (tasks.size() - completedCount));

// Week6 extra: show sorted tags + most frequent

if (!allTags.isEmpty()) {

System.out.println();

System.out.println("Sorted tags: " + new TreeSet<>(allTags));

}

if (!tagFrequency.isEmpty()) {

System.out.println();

System.out.println("Sorted tag frequencies (by tag):");

TreeMap<String, Integer> sorted = new TreeMap<>(tagFrequency);

for (Map.Entry<String, Integer> e : sorted.entrySet()) {

System.out.println(e.getKey() + " = " + e.getValue());

}

viewMostFrequentTag();

}

if (!tasks.isEmpty()) {

System.out.println();

System.out.println("Main task list:");

viewAllTasks();

}

if (!processingQueue.isEmpty()) {

System.out.println();

System.out.println("FIFO queue:");

viewProcessingQueue();

}

if (!priorityQueue.isEmpty()) {

System.out.println();

System.out.println("Priority queue:");

viewPriorityQueue();

}

if (!recentActions.isEmpty()) {

System.out.println();

System.out.println("Most recent activities (up to 10):");

int shown = 0;

for (int i = recentActions.size() - 1; i >= 0 && shown < 10; i--) {

System.out.println(recentActions.get(i));

shown++;

}

}

}

// ------------------------------------------------------------

// Helper methods

// ------------------------------------------------------------

private PriorityQueue<Task> createPriorityQueue() {

return new PriorityQueue<>(

Comparator.comparingInt(Task::getPriorityValue).reversed()

.thenComparing(Task::getTitle, String.CASE_INSENSITIVE_ORDER));

}

private void refreshPriorityQueueEntry(Task task) {

if (priorityQueue.remove(task)) {

priorityQueue.offer(task);

}

}

/**

* Rebuild allTags and tagFrequency from current tasks.

* This keeps Set/Map summaries consistent even after edits/removals.

*/

private void refreshTagData() {

allTags.clear();

tagFrequency.clear();

for (Task task : tasks) {

for (String tag : task.getTags()) {

allTags.add(tag);

tagFrequency.put(tag, tagFrequency.getOrDefault(tag, 0) + 1);

}

}

}

private int readInt(String prompt) {

while (true) {

System.out.print(prompt);

String line = input.nextLine().trim();

try {

return Integer.parseInt(line);

} catch (NumberFormatException ex) {

System.out.println("Please enter a valid integer.");

}

}

}

private String readNonEmptyLine(String prompt) {

while (true) {

System.out.print(prompt);

String line = input.nextLine().trim();

if (!line.isEmpty()) return line;

System.out.println("Input cannot be empty.");

}

}

private String readTag(String prompt) {

// Normalize tags to lower-case so "Urgent" and "urgent" become the same tag.

return readNonEmptyLine(prompt).toLowerCase();

}

private void displayTask(Task task, int index) {

System.out.printf(

"%d id=%d | %s | completed=%s | priority=%s | tags=%s%n",

index,

task.getId(),

task.getTitle(),

task.isCompleted(),

task.getPriority(),

formatTags(task));

}

private void displayTask(Task task) {

System.out.printf(

"id=%d | %s | completed=%s | priority=%s | tags=%s%n",

task.getId(),

task.getTitle(),

task.isCompleted(),

task.getPriority(),

formatTags(task));

}

private String formatTags(Task task) {

if (task.getTags().isEmpty()) return "\[\]";

// Display tags in sorted order for readability (TreeSet view)

return new TreeSet<>(task.getTags()).toString();

}

private int chooseTaskIndex() {

if (tasks.isEmpty()) {

System.out.println("No tasks available.");

return -1;

}

viewAllTasks();

int index = readInt("Enter task index: ");

if (index < 0 || index >= tasks.size()) {

System.out.println("Invalid task index.");

return -1;

}

return index;

}

private PriorityLevel choosePriorityLevel() {

System.out.println("1. HIGH");

System.out.println("2. MEDIUM");

System.out.println("3. LOW");

int choice = readInt("Choose a priority: ");

return switch (choice) {

case 1 -> PriorityLevel.HIGH;

case 2 -> PriorityLevel.MEDIUM;

case 3 -> PriorityLevel.LOW;

default -> {

System.out.println("Invalid choice. Defaulting to LOW.");

yield PriorityLevel.LOW;

}

};

}

// ------------------------------------------------------------

// Nested model types

// ------------------------------------------------------------

private enum ActionType {

ADD,

EDIT,

REMOVE,

COMPLETE,

MOVE_TO_QUEUE,

PROCESS_QUEUE,

ASSIGN_PRIORITY,

ADD_TO_PRIORITY_QUEUE,

PROCESS_PRIORITY_QUEUE,

ADD_TAG

}

private enum PriorityLevel {

HIGH(3),

MEDIUM(2),

LOW(1);

private final int value;

PriorityLevel(int value) {

this.value = value;

}

public int getValue() {

return value;

}

}

private static class Task {

private final int id;

private String title;

private boolean completed;

private PriorityLevel priority;

// Week 6: tags stored in a Set => no duplicates

private final Set<String> tags = new HashSet<>();

public Task(int id, String title) {

this.id = id;

this.title = title;

this.completed = false;

this.priority = PriorityLevel.LOW;

}

public int getId() { return id; }

public String getTitle() { return title; }

public void setTitle(String title) { this.title = title; }

public boolean isCompleted() { return completed; }

public void setCompleted(boolean completed) { this.completed = completed; }

public PriorityLevel getPriority() { return priority; }

public void setPriority(PriorityLevel priority) { this.priority = priority; }

public int getPriorityValue() { return priority.getValue(); }

// Set-based operations

public boolean addTag(String tag) { return tags.add(tag); }

public boolean hasTag(String tag) { return tags.contains(tag); }

public Set<String> getTags() { return tags; }

@Override

public String toString() {

return "id="

  • id

  • " | "

  • title

  • " | completed="

  • completed

  • " | priority="

  • priority

  • " | tags="

  • new TreeSet<>(tags);

}

}

private static class ActionRecord {

private final ActionType actionType;

private final Task task;

private final String details;

public ActionRecord(ActionType actionType, Task task, String details) {

this.actionType = actionType;

this.task = task;

this.details = details;

}

@Override

public String toString() {

String taskSummary = task == null ? "n/a" : ("id=" + task.getId() + ", title=" + task.getTitle());

return actionType + " | task=" + taskSummary + " | details=" + details;

}

}

}

Week 8

0. 本周主题( Week 08 Theme

  • 前两周学习了经典数据结构(classic data structures):列表(lists)、栈(stacks)、队列(queues)、优先队列(priority queues)、集合(sets)、映射(maps),并用它们解决问题。
  • 本周转向常见算法设计技巧(algorithmic techniques):动态规划(dynamic programming)、分治(divide-and-conquer)、回溯(backtracking),目标是开发高效算法(efficient algorithms)。

目标( objectives

  • 从大 O(Big O)与增长率(growth-rate analysis)开始,学会用复杂度(complexity)比较算法。
  • 经典例子(classic examples):二分查找(binary search)、选择排序(selection sort)、汉诺塔(Tower of Hanoi)。
  • 深入主题(study):动态规划(dynamic programming)、欧几里得算法(Euclidean algorithm)、埃氏筛(sieve of Eratosthenes)、分治(divide-and-conquer)。
  • 最后(finish):八皇后(Eight Queens)、凸包构造(convex hull construction)、字符串匹配(string matching)。

1. 衡量算法效率( Measuring Algorithm Efficiency

1.1 什么是算法效率

  • 问题:两个算法做同一件事(same task),比如线性查找 vs 二分查找(linear search vs binary search),哪个更好?
  • 直接跑程序比执行时间(execution time)不可靠,原因有两点:
    1. 多任务并发(concurrency):程序实际运行时间受系统负载(system load)影响。
    2. 输入相关(input-specific):例如目标刚好在开头,线性查找可能比二分查找更快。
  • 因此需要理论分析(theoretical approach):让比较独立于具体机器与特定输入(independent of computers and specific input),关注输入规模变化的影响(effect of input size change)。
  • 用增长率(growth rates)来比较:看输入变大时,运行时间增长有多快(how fast runtime increases as input size increases)。
  • 线性查找逐个比较(sequentially compares),直到找到或数组耗尽(found or exhausted)。
  • 若 key 不在数组:需要 n 次比较(n comparisons, worst case)。
  • 若 key 在数组:平均需要 n/2 次比较(n/2 comparisons on average)。
  • 结论:执行时间与数组大小成正比(proportional to n);数组规模翻倍,比较次数也近似翻倍(double n → double comparisons)。
  • 所以是线性增长(linear rate),数量级(order of magnitude)是 n。

2. Big O 记号( Big O Notation

2.1 Big O 的三种分析( cases

  • Big O 的组织方式:
    1. 先描述增长率(describe growth rate)
    2. 再说明讨论的是最好/平均/最坏(best/average/worst case)
  • 三种情况(cases):
    • 最好情况(best case):最短时间(shortest time)
    • 平均情况(average case):典型时间(typical time)
    • 最坏情况(worst case):最长时间(longest time),并且是最常用(most used),因为给出安全上界(safe upper bound)。
  • 同一输入规模(same input size)下,不同输入会触发不同情况(different inputs trigger different cases),所以执行时间仍可能变化。

2.2 Big O 要忽略什么( What to Ignore

目的:只保留控制长期增长的部分(keep only the part that controls long-term growth)

  • 忽略乘法常数(multiplicative constants):O(n)=O(n/2)=O(100n) 同一数量级(same order of magnitude)
    • 因为增长比例(growth ratio)一样:n 翻倍时都翻倍(doubling n doubles them)
  • 忽略非主导项(nondominating terms):n-1 → O(n) 因为 n 主导(dominant term)
  • 常数时间(constant time)运行时间不随 n 变化, 是 O(1)
  • 常见求和(summations):
    • 1 + 2 + ... + (n−1) = n(n−1)/2 → O(n²)
    • 1 + 2 + ... + n = n(n+1)/2 → O(n²)

2.3 时间与空间( Time vs Space Complexity

  • 时间复杂度(time complexity):算法运行多久(how long)。
  • 空间复杂度(space complexity):算法用多少内存(how much memory)。
  • 例子强调:输入数组随着 n 增长会占更多格子(allocated cells grows with n),所以空间是 O(n);少量辅助变量(helper variables)是 O(1)。

3. 如何判断 Big O :循环与分支( Determining Big O

3.1 单层循环( single loop

  • 示例:for (i = 1; i <= n; i++) { k = k + 5; }
  • 单次更新是常数成本 c(constant cost c)。
  • 循环执行 n 次:T(n) = c * n → O(n)。

3.2 双层循环( double loop

  • 示例:for i=1..n 内层 for j=1..n,每次做常数操作。
  • 总次数 n × n:T(n) = c * n * n → O(n²)。
  • 截图也用"执行网格(execution grid)"展示工作量随 n² 增长(quadratic growth)。

3.3 依赖型双循环( dependent nested loop

  • 示例:内层跑到 j <= i(inner loop depends on i)。
  • 总工作量是三角形求和(triangular sum):c + 2c + ... + nc。
  • 化简仍是 O(n²)(still quadratic)。

3.4 内层是常数( constant inner loop

  • 示例:内层固定执行 20 次(inner loop fixed at 20),外层执行 n 次。
  • T(n) = 20 * c * n → O(n)。
  • 关键点:固定次数只算常数因子(constant factor),不改变 Big O。

3.5 顺序两段代码( two loop sequences

  • 先一个固定 10 次的循环(fixed loop 10c),再一个"外层 n 次、内层固定 20 次"的结构(20cn)。
  • 总和:10c + 20cn → 由主导项决定(dominant term)→ O(n)。
  • 关键点:顺序执行是加法(additive),最后看增长最快的那段(fastest-growing part)。

3.6 分支( selection statement )按最坏路径( worst-case path

  • if 条件用了 list.contains(e):对 n 个元素的 list 是 O(n)。
  • else 分支里也遍历 list:也是 O(n)。
  • 分支分析看最坏路径(worst-case path):O(n) + O(n) → O(n)。
  • 核心结论:分支不取平均,而取最贵路径(take the most expensive path)。

3.7 对数级( logarithmic )例:重复平方( repeated squaring

  • 计算 a^n:朴素方法乘 n 次是 O(n)。
  • 改进方法:重复平方(repeated squaring)把循环次数降到 log n(reduce loop count to log n)。
  • 结论:改进后是 O(log n)(logarithmic)。
  • 截图也强调:对数增长很慢(grows very slowly),对数底数不影响 Big O(log base doesn't matter in Big O)。

4. 经典复杂度例子( Classic Examples

二分查找(Binary Search)用于已经排序的数组(sorted array)。它每一轮只做固定数量的工作,同时把剩余搜索范围减半。

  • 每轮做常数工作并"折半"(halve):递推式 T(n)=T(n/2)+c → O(log n)。

4.2 选择排序( Selection Sort

选择排序(Selection Sort)每一轮从未排序部分中找到最小值,然后把它交换到正确位置。

  • 每一趟找最小值:比较次数 (n-1)+(n-2)+...+1 → O(n²)。

4.3 汉诺塔( Tower of Hanoi

要求把 n 个盘子从塔 A 移动到塔 B,并借助塔 C。

  1. 先把上面的 n - 1个盘子从 A 移到 C。
  1. 把最大的第 n 个盘子从 A 移到 B。
  2. 再把 n - 1 个盘子从 C 移到 B。
  • 递推:T(n)=2T(n-1)+1 → O(2ⁿ) 指数级(exponential)。
  • 文档强调指数增长非常快:盘子数增加会导致移动次数爆炸。

4.4 常见递推关系速判( common recurrences

  • T(n)=T(n/2)+O(1) → O(log n)(binary search / Euclid)。
  • T(n)=T(n-1)+O(1) → O(n)(linear search)。
  • T(n)=2T(n/2)+O(n) → O(n log n)(merge sort)。
  • T(n)=2T(n-1)+O(1) → O(2ⁿ)(Hanoi)。

4.5 常见增长函数对比( growth functions

文档用 n=25→50 的变化展示:

从慢到快大致是:O(1) < O(log n) < O(n) < O(n log n) < O(n²) < O(n³) < O(2ⁿ)

  • Best scaling(最佳扩展性):O(1), O(log n)
  • Moderate scaling(中等扩展性):O(n), O(n log n)
  • Poor scaling(较差扩展性):O(n²), O(n³), O(2ⁿ)

5. 动态规划( Dynamic Programming

5.1 斐波那契:朴素递归( naive recursion

直接递归计算斐波那契数(recursive Fibonacci)很容易写,但效率很低,因为它会重复计算大量相同的子问题。

原始递归方法

  • fib(0) = 0

  • fib(1) = 1

  • fib(n) = fib(n - 1) + fib(n - 2)

  • 递归 fib:T(n)=T(n-1)+T(n-2)+c,文档指出会接近 O(2ⁿ),因为反复计算相同子问题(repeated subproblems)。

5.2 斐波那契:动态规划改进( DP Fibonacci

  • 通过保存连续的 f0,f1,f2 并迭代更新(iteratively update consecutive values),避免重复递归,时间降为 O(n)。
  • DP 核心:子问题(subproblems)只求一次并复用(store and reuse)。

6. 最大公约数 GCD Greatest Common Divisor

最大公约数(Greatest Common Divisor, GCD)是能够同时整除两个整数的最大整数。

6.1 暴力法( brute force

  • 逐个试除数 k:最多跑到 n,时间 O(n)。

6.2 欧几里得算法( Euclid's algorithm

欧几里得算法(Euclid's Algorithm)用反复取余(remainder reduction)代替逐个检查除数。

  • 规则:若 m%n==0,则 gcd(m,n) = n;否则 gcd(m,n)=gcd(n,m%n)。
  • 复杂度:最坏也只有 O(log n),因为参数会快速变小(shrinks quickly)。

6.3 最坏情况( worst case

  • 最长递归链出现在相邻斐波那契数(successive Fibonacci numbers),但仍是 O(log n)。

比较 GCD Algorithms Compare

主要区别 Main Difference

前两种方法都属于除数扫描(divisor scanning):

  • 第一种检查所有可能除数。
  • 第二种减少了一部分检查范围。
  • 但它们仍然是在一个个试除数,所以最坏情况下还是线性时间。

欧几里得算法不同,它改变了问题结构:

  • 不再扫描候选除数
  • 使用余数不断缩小问题
  • 每一步都把数字变小很多

7. 质数( Prime Numbers

大于 1 的整数如果只有两个正因数:

  • 1
  • 它自己

7.1 早期版本:试除到 n/2 divisors up to n/2

  • 文档回顾:检查前 50 个质数时,对每个候选数试除到 number/2,代价很大。

程序不断增加 number:

  1. 假设当前数字是素数。
  2. 用 divisor 从 2 检查到 number / 2。
  3. 如果发现 number % divisor == 0,说明它不是素数。
  4. 如果没有找到任何除数,就打印该素数并增加计数。
  5. 当已经找到 50 个素数时停止。

7.2 改进:试除到 √n up to sqrt

  • 判断质数只需要试除到 √number:若 2..√n 无因子,则为质数;复杂度从 O(n) 降到 O(√n)。
  • 如果 number 有一个大于 √number 的因子,那么它一定对应一个小于 √number 的因子

7.3 再改进:只用已发现的质数做除数( prime divisors only

  1. 避免重复计算平方根边界(avoid recomputing square-root boundary)。
  1. 只用已经发现的素数作为除数(prime divisors only)。

改进一:缓存平方根 SquareRoot Cache

其实 √97 一直都是同一个值,没必要反复算。

第一版程序可能在循环中反复计算:Math.sqrt(number)

改进方法是维护一个变量:squareRoot

只有当 number 跨过新的完全平方数(perfect square)时才更新它。

例如 int(Math.sqrt(number)) 只会在 number 跨过:

4, 9, 16, 25, 36, 49, ...时改变。

不用每次都调用 Math.sqrt(number),只要检查:if (squareRoot * squareRoot < number) squareRoot++; 来减少重复计算。

改进二:只检查素数除数 Prime Divisors Only

如果一个数是合数(composite ),它一定有一个不超过 √n 的素因子(prime factor)。

因此没必要尝试所有整数除数,只需要尝试已经找到的素数列表(list of primes)中的素数。

复用结果 Reuse Results

程序把已经发现的素数保存到 list 中,后续判断新数字时复用这些结果。

这有点像动态规划思想(dynamic-programming-style improvement):之前的计算结果会变成之后问题的子结果。

复杂度思想

文档中给出的估计是:

不超过 n 的素数数量大约是:

n / log n

不超过 √n 的素数数量大约是:

2√n / log n

因此每个数的素数除数检查数量比检查所有整数除数少很多。

整体结果

文档给出的整体复杂度形式是:

O(n√n / log n)

7.4 埃拉托色尼筛法( Sieve of Eratosthenes

埃拉托斯特尼筛法(Sieve of Eratosthenes)通过在布尔数组中标记合数倍数,找出 n 以内所有素数。

  • 核心思想

与其单独测试每一个候选数,不如一次性"筛掉"所有合数(composite multiples)。

程序维护一个布尔数组:primes\[\]

初始时假设所有位置都是 prime。

然后从 2 开始,如果当前数仍被标记为 prime,就把它的倍数标记为 false。

  • 筛法步骤
    1. 建立布尔数组(boolean array),先假设每个数都是素数。
    2. 从 base = 2 开始。
    3. 如果 base 是素数,就把它的倍数标记为非素数。
    4. 只需要处理到 k * k <= n。
    5. 最后数组中仍为 true 的位置就是素数。
  • 为什么只到平方根

如果一个合数大于 √n 的因子才被发现,那么它必然还有一个小于 √n 的因子已经处理过。

所以循环条件可以是:

k * k <= n

  • 宽松上界 Loose Upper Bound

文档中用一个宽松上界说明筛法效率:n/2 + n/3 + n/5 + n/7 + ...

这个和小于:O(n log n)

文档进一步给出更好的说明:筛法通常远好于逐个测试每个数,实际常用复杂度理解更接近:O(n log log n)

但文档比较表使用的是保守上界。

8. 分治:最近点对( Divide-and-Conquer: Closest Pair

最近点对问题(Closest Pair of Points )要求在一组点中找到欧氏距离(Euclidean distance)最小的两个点。

8.1 暴力( brute force

  • 两两比较距离:约 n(n-1)/2 次 → O(n²)。

8.2 分治( divide-and-conquer

算法先按 x 坐标排序(sort by x-coordinate),然后:

  1. 将点集分成左右两半。
  2. 分别递归求左半部分和右半部分的最近点对。
  3. 得到左右两边的最小距离 d1 和 d2。
  4. 令 d = min(d1, d2)。
  5. 只检查中线附近宽度为 d 的窄条区域(narrow strip)。
  6. 返回左侧、右侧、跨中线三种情况中的最小值。
  • 为什么只检查窄条

如果最近点对跨过分割线,那么它们距离必须小于当前最小值 d。

因此只有离中线距离不超过 d 的点才可能组成更优解。

  • 合并步骤为什么是线性

把 strip 中的点按 y 坐标处理(y-order)时,每个点只需要与附近有限数量的候选点比较。

因此合并工作是:O(n)

  • 递推:T(n)=2T(n/2)+O(n) → O(n log n)。

9. 回溯:八皇后( Backtracking: Eight Queens

八皇后问题(Eight Queens Problem)是一个回溯问题(backtracking problem)。目标是在 8×8 棋盘上放置 8 个皇后,使它们互不攻击。

状态表示 State

文档使用一维数组:

queens\[\]

其中:

queensi

表示第 i 行(row i)的皇后放在哪一列(column)。

算法按行放置皇后:

  1. 从第 0 行开始。
  2. 在当前行尝试每一列。
  3. 如果当前位置合法,就递归搜索下一行。
  4. 如果后续失败,就撤销当前位置并尝试下一列。
  5. 如果所有行都成功放置,说明找到完整解。

成功与失败

  • If Successful:移动到下一行;如果当前行是最后一行,说明找到了完整解。
  • If Not Successful:回到上一行,继续尝试下一列。
  • If The First Row Fails:如果第 0 行没有任何可行列,则说明无解。

合法性检查 isValid

一个位置必须满足:

  • 不能与之前皇后在同一列。
  • 不能在同一对角线。

对角线判断使用:

Math.abs(queensi - queensrow) == row - i

10. 凸包( Convex Hull

10.1 问题定义( definition

  • 凸包(convex hull)是包含一组点的最小凸多边形(smallest convex polygon)。
  • 给定一组点,凸包是能包住所有点的最小外边界。

如果一个点位于边界上,它就是凸包的一部分;内部点不会影响凸包轮廓。

黑点代表普通点(ordinary point),蓝点代表凸包顶点(hull vertex),蓝色边界表示凸包边界(convex hull boundary)

  • 观察重点

移动或添加一个外部点,可能立即改变整个多边形边界;

但如果添加的是内部点,凸包可能不变。

10.2 礼物包装( Gift-wrapping / Jarvis march

  • 从"最右且最低点"(rightmost lowest point)开始,重复扫描所有点找下一个最外点(outermost)。
  • 步骤
    1. 选择最右下点(rightmost lowest point)作为起始 hull vertex。
    2. 扫描所有点,找到从当前顶点出发的下一个最外侧点。
    3. 重复,直到回到起点。
  • 复杂度:若凸包顶点数为 h,总点数 n,则 O(hn)。

10.3 Graham 扫描( Graham's algorithm / Graham scan

Graham 算法(Graham's Scan)先选一个锚点(anchor point),按极角排序,再用栈保留凸包边界。

核心思想

先选择最右下点作为锚点 p0,然后将其他点按相对于 p0 的角度排序(polar angle order)。

之后按顺序扫描点:

  • 如果形成左转(left turn),保留。
  • 如果形成右转或向内弯(bend inward),弹出栈顶点。
  • 最终栈中剩下的点就是凸包顶点。

步骤

  1. 选择锚点 p0。
  2. 按极角对剩余点排序。
  3. 先把前三个点压入栈。
  4. 继续处理剩余点:
    • 遇到右转就 pop。
    • 遇到左转就 push。
  5. 栈中点构成最终 hull。
  • 排序 O(n log n) + 扫描线性(each point pushed once, popped at most once)→ 总 O(n log n)。

优于 Gift-Wrapping 的 O(hn)

11. 字符串匹配( String Matching

11.1 暴力匹配( brute force

  • 文本长度是 n
  • 模式串长度是 m

最坏情况下有:n - m + 1个对齐位置,每个位置最多比较 m 个字符。

  • 对 text 的每个对齐位置 i(alignment)尝试比较 pattern 的每个字符,找到第一个匹配位置;最坏 O(nm)。

11.2 Boyer--Moore (坏字符规则 bad-character shift

  • 从右往左比(right-to-left compare),利用不匹配字符决定跳跃(shift farther)。
  • 核心思想

比较方向从 pattern 的右端开始:

right to left

如果发生不匹配(mismatch),就根据文本中的失配字符决定移动距离。

  • 坏字符规则 Bad-Character Shift

文档中的演示强调:

  • 如果失配的文本字符没有出现在 pattern 中,可以直接把 pattern 跳过该字符。
  • 如果该字符出现在 pattern 更早的位置,就把 pattern 中的最后一次出现对齐到该文本字符。
  • 两种情况

Case 1:失配文本字符不在 pattern 剩余部分中

→ pattern 可以整体跳过该字符。

Case 2:失配字符在 pattern 中更早出现

→ 将 pattern 中该字符最后一次出现的位置与文本字符对齐。

  • 文档说明:平均更快,但该简化版本最坏仍可能 O(nm)。

11.3 KMP Knuth--Morris--Pratt

KMP(Knuth-Morris-Pratt)算法通过预处理 pattern 得到失败函数(failure function),从而在失配后复

用已经成功匹配的前缀信息。

  • 核心思想

暴力法或 Boyer-Moore 在失配时可能重新开始一个新的对齐。

KMP 不让文本指针回退,而是根据 pattern 的已知结构移动 pattern 指针。

也就是说,KMP 会复用:

successful partial matches

  • Failure Array

失败数组(failure array)记录 pattern 中每个位置之前,最长的相同前缀/后缀关系。

当发生失配时,算法不回到 pattern 开头,而是跳到:

failk - 1

对应的位置继续比较。

  • 搜索过程

设:

  • i 是 text 指针
  • k 是 pattern 指针

如果字符匹配:

  • i++
  • k++

如果不匹配且 k > 0:

  • k = failk - 1

如果不匹配且 k == 0:

  • i++
  • 成本 Cost

失败函数预处理和搜索过程加起来是线性的:O(n + m)

  • 为什么 KMP 不同

KMP 在长的部分匹配后发生失配时,可以立即退回到较短但已知有效的前缀状态,而不是从头开始。

这就是它比暴力匹配更高效的原因。

12. 总结:算法设计的主线( Main Design Lessons

  • 选择算法时问:输入规模缩小得有多快(how fast input shrinks),能否复用以前的计算(reuse previous work),是否能剪枝(prune bad branches),主导项(dominant term)如何随 n 增长。
  • 文档归纳:最大提升通常来自------更激进地减小问题规模(aggressive reduction)、存储可复用中间结果(store reusable intermediates)、预计算结构(precompute structure)、减少无谓比较(avoid unnecessary comparisons)。

lab sheet

0) 实验定位与主题( Lab purpose & theme

  • 本实验是在 Week 6 系统基础上继续扩展(extend Week 6 system),引入:
    • 大 O 记号(Big O notation):时间复杂度(time complexity)与空间复杂度(space complexity)
    • 面向性能的重构(performance-aware refactoring)
    • 基于效率的显式约束(explicit design constraints based on efficiency)
    • "更快查询 vs 更多内存"的权衡(trade-offs: faster queries vs extra memory)
  • 要求:必须继续使用 Weeks 5&6 的同一个 todo 系统(same todo management system),不能新建程序(do not create a new program)。每个任务都是在原系统上优化(improve existing feature/data-access pattern)。
  • Week 8 核心:从"正确性(correctness)"转向"效率(efficiency)",分析瓶颈(bottlenecks)并让常用操作可扩展(scale efficiently)。

1) 复杂度符号( Complexity symbols

文档规定在分析中使用这些符号(symbols):

  • n:主系统任务数量(number of tasks)
  • u:唯一标签数(number of unique tags)
  • T:全系统标签绑定总数(total tag assignments across all tasks)
  • k:某个 tag 匹配到的任务数量(tasks matching a given tag)
  • m:单个任务拥有的标签数(tags on one selected task)
  • q:FIFO 队列中的任务数(tasks in FIFO queue)
  • p:优先队列中的任务数(tasks in priority queue)
  • 默认:用最坏情况(worst-case),忽略常数和非主导项(ignore constants, nondominating terms)。

2) Task 1 :现有系统复杂度审计( Complexity audit

2.1 场景( Scenario

  • 改进前先找会随输入变大变慢的操作(identify operations expensive as input grows)。

2.2 必做内容( Required features

你要做:

  1. 指出选定操作的当前时间复杂度(time complexity)。
  2. 说明主结构的辅助空间(auxiliary space)。
  3. 找出至少 3 个可能在大输入下低效的操作(inefficient at scale)。
  4. 把分析记录在复杂度表(complexity table):注释/markdown/report 均可。

2.3 指定要分析的方法( Methods to analyze

至少包括(at least):

  1. lookupTaskById()
  2. viewTasksByStatus()
  3. filterTasksByTag()
  4. viewAllTags()
  5. viewTagFrequency()
  6. viewSortedTags()
  7. moveTaskToQueue() 的重复检查(duplicate check)
  8. addTaskToPriorityQueue() 的重复检查(duplicate check)

2.4 约束( Constraints

  • 先不要改变用户可见行为(do not change user-visible behaviour yet)。
  • 以 Week 6 完整系统为基线(baseline)。
  • 每个方法写:最坏时间(worst-case time)、必要时的平均情况说明(average-case notes for hash)、额外空间(extra space)。
  • 忽略逐字符打印成本(ignore character-by-character output cost)。

2.5 预期结果( Expected outcome

能区分:

  • 已经高效的(already efficient),如 map-based ID lookup
  • 重复扫描全系统的(repeated full scans)
  • 用额外内存换速度的(trade memory for speed)

3) Task 2 :用索引加速标签过滤( Fast tag filtering with an index

3.1 问题( Problem

  • Week 6 的 tag 过滤会变慢(inefficient when many tasks),因为每次过滤都可能要检查所有任务(scan every task each time)。

3.2 目标( Goal

  • 建立"标签 → 任务集合"的直接索引(direct tag index)。

3.3 必做( Required features

  1. 添加结构:每个 tag 映射到包含该 tag 的任务(map each tag to tasks containing it)。
  2. 用该结构实现直接过滤(direct filtering)。
  3. 保持索引一致(keep consistent)在:新增 tag、删除任务、未来若支持删 tag。
  4. 显示匹配任务时不要扫描整个主列表(no full scan)。
  5. 安全处理未知 tag 与空结果(handle unknown tags/empty results)。

3.4 建议结构( Suggested structure

  • Map<String, Set<Integer>> tasksByTag = new HashMap<>();
  • Set 里放任务 ID(task IDs),再用现有 taskMap 做 O(1) 查回 Task。

3.5 复杂度要求( Time/space constraints

  • filterTasksByTag(tag):平均 O(1 + k)(hash-based)+ 输出成本。
  • 若用树结构保证有序(tree-based),可为 O(log u + k),但要解释。
  • 新增 tag 后更新索引:平均 O(1)。
  • 删除任务更新索引:O(m)。
  • 额外空间:索引可能增长到 O(T)。
  • 设计提示:HashSet 不保证顺序;若要排序可用树 Set,但更新更贵(higher update cost)。重点是快查(fast access),不是保持原 List 顺序。

4) Task 3 :增量维护标签统计( Incremental tag analytics

4.1 问题( Problem

  • Week 6 的很多报表是每次都"从头重建"(full recomputation / rebuild),小数据可以,大数据会浪费。

4.2 必做( Required features

  1. 让 allTags 随任务/标签变化自动保持最新(keep updated)。
  2. 让 tagFrequency 随变化自动更新(keep updated)。
  3. 不要每次请求报表都扫描所有任务重建(remove rebuild scans)。
  4. 仍需正确支持:查看所有 tags、频率、最频繁 tag、hash vs tree 对比、system overview。
  5. 解释你是偏向"更快更新"还是"更快查询"还是折中(update vs query trade-off)。

4.3 复杂度要求( Time/space constraints

  • 查询类方法不能通过扫描所有任务重建(must not rebuild by scanning every task)。
  • 给一个任务新增一个 tag:平均 O(1) 更新统计。
  • 删除任务:O(m) 更新统计。
  • viewTagFrequency():在维护好后应为 O(u)。
  • viewMostFrequentTag():
    • 可以 O(u)(每次扫描 frequency map),或
    • O(1)(维护额外状态),但要解释额外空间与更新成本。

4.4 有序视图策略( Sorted view strategy

  • 方案 1:按需排序(sort on demand)------展示时临时建 TreeSet/TreeMap(updates simpler, queries slower)。
  • 方案 2:持续维护有序结构(maintain continuously)------查询快但更新更贵、内存更多(faster sorted queries, expensive updates, more memory)。
  • 需要用 Big O 简要论证选择。

5) Task 4 :更快的状态访问( Faster status-based access

5.1 问题( Problem

  • Week 6 按完成/未完成显示是扫描整个任务列表(scan full list),频繁调用浪费。

5.2 必做( Required features

  1. 加一个"完成/未完成索引"(status index)。
  2. 在新增任务、标记完成、删除任务时更新索引(keep consistent)。
  3. 用索引展示,不扫描全表(no full scan)。
  4. 菜单行为和输出语义保持一致(same behaviour/meaningful output)。
  5. 正确处理空结果(handle empty results)。

5.3 建议结构( Implementation constraints

二选一:

  • Set<Integer> completedTaskIds + Set<Integer> incompleteTaskIds
  • Map<Boolean, Set<Integer>> tasksByCompletion
    并复用 taskMap 做快速取 Task(efficient retrieval)。

5.4 复杂度要求( Time/space constraints

  • 查看 completed:O(c)(c=完成任务数)。
  • 查看 incomplete:O(i)(i=未完成任务数)。
  • 标记完成:平均 O(1) 更新索引。
  • 额外空间:O(n)。

6) 可选挑战:更快的队列重复检查( Optional: Faster queue membership checks

6.1 问题( Problem

  • Week 6 里 FIFO queue / priority queue 的 contains() 检查是线性的(linear):
    • LinkedList contains → O(q)
    • PriorityQueue contains → O(p)

6.2 目标( Goal

  • 用额外结构让重复检查变成平均 O(1)(constant average time)。

6.3 建议结构( Suggested structures

  • Set<Integer> queuedTaskIds
  • Set<Integer> priorityQueuedTaskIds

6.4 约束( Constraints

  • Membership checks:平均 O(1)。
  • 插入/移除时必须保持 set 一致(keep consistent)。
  • 额外空间:O(q + p)。

7) 提交要求与复杂度表( Submission requirements & complexity table

7.1 你要提交( Submit

  1. 更新后的 Java 程序(updated program)。
  2. 一份复杂度总结(complexity summary):改进特性的主要时间/空间复杂度。
  3. 至少一个设计权衡解释(design trade-off explanation):
    • hash vs tree
    • on-demand recompute vs maintaining index
    • faster query vs extra memory

7.2 文档建议的复杂度表模板( Suggested table

示例行包括:

  • Look up by ID:O(1) avg → O(1) avg(existing map)
  • Filter by tag:≈ O(T + n) → O(1 + k) avg(extra space O(T))
  • View all tags:O(T) → O(u)(existing tag set)
  • View tag frequency:O(T) → O(u)(existing frequency map)
  • View tasks by status:O(n) → O(c) / O(i)(extra space O(n))
  • Queue duplicate check:O(q) → optional O(1) avg(extra space O(q))

7.3 讨论题( Discussion questions

  • 为什么 lookupTaskById 在任务变大时仍高效(efficient at large n)?
  • 为什么 tag index 能提升重复过滤(improves repeated filtering)?
  • 哪个操作因为多用内存而变快(faster due to extra memory)?
  • 如果经常需要排序输出,什么时候 tree 值得(tree-based worth it)?
  • 为什么写 Big O 忽略常数(ignore constants)?

TodoSystemWeek8Improved

import java.util.ArrayList;

import java.util.Comparator;

import java.util.HashMap;

import java.util.HashSet;

import java.util.LinkedHashSet;

import java.util.LinkedList;

import java.util.List;

import java.util.Map;

import java.util.PriorityQueue;

import java.util.Queue;

import java.util.Scanner;

import java.util.Set;

import java.util.Stack;

import java.util.TreeMap;

import java.util.TreeSet;

// Week8 改进版:用索引与增量维护提升查询效率,用空间换时间。

// Week8 improved: use indexes + incremental maintenance to speed up queries (space-time tradeoff).

public class TodoSystemWeek8Improved {

// 输入读取器:使用 nextLine+parseInt 避免 nextInt/nextLine 混用问题。

// Input reader: use nextLine+parseInt to avoid nextInt/nextLine newline issues.

private final Scanner input = new Scanner(System.in);

// 主任务列表:用于按索引展示与编辑(保持插入顺序)。

// Main task list: keeps insertion order for index-based UI operations.

private final List<Task> tasks = new ArrayList<>();

// FIFO 处理队列:按先进先出处理任务。

// FIFO processing queue: tasks are processed in first-in-first-out order.

private final Queue<Task> processingQueue = new LinkedList<>();

// 动作历史栈:用于记录最近操作(后进先出)。

// Action history stack: records recent actions in LIFO order.

private final Stack<ActionRecord> recentActions = new Stack<>();

// 优先队列:按 priorityValue(高→低)再按 title 排序。

// Priority queue: orders by priorityValue (high→low) then by title.

private final PriorityQueue<Task> priorityQueue = createPriorityQueue();

// Week6:ID→Task 的查找表,平均 O(1) 按 ID 取任务。

// Week6: ID→Task map for O(1) average lookup by ID.

private final Map<Integer, Task> taskMap = new HashMap<>();

// Week8:唯一标签集合(插入顺序),避免每次重建标签集合。

// Week8: unique tag set (insertion order), avoids rebuilding tags repeatedly.

private final Set<String> allTags = new LinkedHashSet<>();

// Week8:标签出现次数统计,增量维护用于快速输出频率。

// Week8: tag frequency counter, incrementally maintained for fast reporting.

private final Map<String, Integer> tagFrequency = new HashMap<>();

// Week8:tag→任务ID集合索引,过滤从 O(n) 降为平均 O(1+k)。

// Week8: tag→task IDs index, filtering drops from O(n) to avg O(1+k).

private final Map<String, Set<Integer>> tasksByTag = new HashMap<>();

// Week8:完成任务ID集合,用于按状态直接访问(不扫描全 tasks)。

// Week8: completed task IDs set for direct status view (no full scan).

private final Set<Integer> completedTaskIds = new LinkedHashSet<>();

// Week8:未完成任务ID集合,用于按状态直接访问(不扫描全 tasks)。

// Week8: incomplete task IDs set for direct status view (no full scan).

private final Set<Integer> incompleteTaskIds = new LinkedHashSet<>();

// 可选挑战:队列成员ID集合,避免 LinkedList.contains 的 O(q)。

// Optional: queued IDs set avoids LinkedList.contains O(q) duplicate checks.

private final Set<Integer> queuedTaskIds = new HashSet<>();

// 可选挑战:优先队列成员ID集合,避免 PriorityQueue.contains 的 O(p)。

// Optional: priority-queued IDs set avoids PriorityQueue.contains O(p) duplicate checks.

private final Set<Integer> priorityQueuedTaskIds = new HashSet<>();

// 自增任务ID:保证每个任务有唯一ID。

// Auto-increment task ID: ensures each task has a unique ID.

private int nextTaskId = 1;

// 程序入口:创建系统并启动主循环。

// Entry point: create system instance and start the main loop.

public static void main(String\[\] args) {

TodoSystemWeek8Improved system = new TodoSystemWeek8Improved();

system.run();

}

// 主循环:菜单驱动(menu-driven)直到用户选择退出。

// Main loop: menu-driven interaction until the user chooses to exit.

public void run() {

boolean running = true;

while (running) {

printMainMenu();

int choice = readInt("Choose an option: ");

switch (choice) {

case 1:

addTask();

break;

case 2:

viewAllTasks();

break;

case 3:

editTask();

break;

case 4:

removeTask();

break;

case 5:

markTaskCompleted();

break;

case 6:

viewTasksByStatus();

break;

case 7:

moveTaskToQueue();

break;

case 8:

viewProcessingQueue();

break;

case 9:

viewNextQueueTask();

break;

case 10:

processNextQueueTask();

break;

case 11:

viewMostRecentAction();

break;

case 12:

removeMostRecentActionFromHistory();

break;

case 13:

viewRecentActionHistory();

break;

case 14:

assignPriorityToTask();

break;

case 15:

addTaskToPriorityQueue();

break;

case 16:

viewPriorityQueue();

break;

case 17:

viewHighestPriorityTask();

break;

case 18:

processHighestPriorityTask();

break;

case 19:

addTagToTask();

break;

case 20:

checkTaskTagMembership();

break;

case 21:

viewAllTags();

break;

case 22:

filterTasksByTag();

break;

case 23:

lookupTaskById();

break;

case 24:

viewTagFrequency();

break;

case 25:

viewMostFrequentTag();

break;

case 26:

viewSortedTags();

break;

case 27:

viewSortedTagFrequencies();

break;

case 28:

compareTagViews();

break;

case 29:

viewSystemOverview();

break;

case 0:

running = false;

System.out.println("Exiting Todo System.");

break;

default:

System.out.println("Invalid option. Please try again.");

}

}

}

// 菜单打印:按 Week5 + Week8 功能分类展示。

// Menu printing: show grouped Week5 + Week8 features.

private void printMainMenu() {

System.out.println();

System.out.println("=== Todo System (Week 8 Improved) ===");

System.out.println("Week 5 - Core List Features");

System.out.println("1. Add task");

System.out.println("2. View all tasks");

System.out.println("3. Edit task");

System.out.println("4. Remove task");

System.out.println("5. Mark task as completed");

System.out.println("6. View tasks by status");

System.out.println();

System.out.println("Week 5 - Queue Features");

System.out.println("7. Move task to processing queue");

System.out.println("8. View processing queue");

System.out.println("9. View next queue task");

System.out.println("10. Process next queue task");

System.out.println();

System.out.println("Week 5 - Stack Features");

System.out.println("11. View most recent action");

System.out.println("12. Remove most recent action from history");

System.out.println("13. View recent action history");

System.out.println();

System.out.println("Week 5 - Priority Queue Features");

System.out.println("14. Assign priority to a task");

System.out.println("15. Add task to priority queue");

System.out.println("16. View priority queue");

System.out.println("17. View highest-priority task");

System.out.println("18. Process highest-priority task");

System.out.println();

System.out.println("Week 8 - Indexed Tag and Status Features");

System.out.println("19. Add tag to a task");

System.out.println("20. Check task tag membership");

System.out.println("21. View all unique tags");

System.out.println("22. Filter tasks by tag");

System.out.println("23. Look up task by ID");

System.out.println("24. View tag frequencies");

System.out.println("25. View most frequent tag");

System.out.println("26. View sorted tags");

System.out.println("27. View sorted tag frequencies");

System.out.println("28. Compare hash-based and tree-based views");

System.out.println("29. View system overview");

System.out.println("0. Exit");

}

// 添加任务:创建 Task 后同步更新 tasks、taskMap、状态索引。

// Add task: create Task and update tasks, taskMap, and status indexes consistently.

private void addTask() {

String title = readNonEmptyLine("Enter task title: ");

Task task = new Task(nextTaskId, title);

nextTaskId++;

tasks.add(task);

taskMap.put(task.getId(), task);

incompleteTaskIds.add(task.getId());

recordAction(ActionType.ADD, task, "Added a new task.");

System.out.println("Task added.");

displayTask(task, tasks.size() - 1);

}

// 查看全部任务:遍历 tasks 列表并按索引打印。

// View all tasks: iterate tasks list and print with list index.

private void viewAllTasks() {

System.out.println("=== All Tasks ===");

if (tasks.isEmpty()) {

System.out.println("No tasks available.");

return;

}

int index = 0;

for (Task task : tasks) {

displayTask(task, index);

index++;

}

}

// 编辑任务:修改 title 后若在优先队列中需刷新堆位置(remove+offer)。

// Edit task: after title change, refresh heap entry if task is in priority queue.

private void editTask() {

int index = chooseTaskIndex();

if (index == -1) {

return;

}

Task task = tasks.get(index);

String oldTitle = task.getTitle();

String newTitle = readNonEmptyLine("Enter the new title: ");

task.setTitle(newTitle);

refreshPriorityQueueEntry(task);

recordAction(ActionType.EDIT, task, "Changed title from '" + oldTitle + "' to '" + newTitle + "'.");

System.out.println("Task updated.");

displayTask(task, index);

}

// 删除任务:必须从主列表、队列、优先队列和所有索引结构中移除以保持一致性。

// Remove task: must remove from list, queues, priority queue, and all indexes for consistency.

private void removeTask() {

int index = chooseTaskIndex();

if (index == -1) {

return;

}

Task task = tasks.remove(index);

boolean removedFromQueue = processingQueue.remove(task);

if (removedFromQueue) {

queuedTaskIds.remove(task.getId());

}

boolean removedFromPriorityQueue = priorityQueue.remove(task);

if (removedFromPriorityQueue) {

priorityQueuedTaskIds.remove(task.getId());

}

removeTaskFromIndexes(task);

String details = "Removed task and updated indexes.";

if (removedFromQueue) {

details += " Also removed from processing queue.";

}

if (removedFromPriorityQueue) {

details += " Also removed from priority queue.";

}

recordAction(ActionType.REMOVE, task, details);

System.out.println("Task removed.");

}

// 标记完成:更新 Task.completed 并把 ID 从 incomplete 移到 completed。

// Mark completed: update Task.completed and move ID from incomplete set to completed set.

private void markTaskCompleted() {

int index = chooseTaskIndex();

if (index == -1) {

return;

}

Task task = tasks.get(index);

if (task.isCompleted()) {

System.out.println("That task is already marked as completed.");

displayTask(task, index);

return;

}

task.setCompleted(true);

incompleteTaskIds.remove(task.getId());

completedTaskIds.add(task.getId());

recordAction(ActionType.COMPLETE, task, "Marked task as completed.");

System.out.println("Task marked as completed.");

displayTask(task, index);

}

// 按状态查看:直接遍历 completedTaskIds/incompleteTaskIds,避免 O(n) 扫描。

// View by status: iterate completed/incomplete ID sets to avoid O(n) scanning.

private void viewTasksByStatus() {

System.out.println("1. View completed tasks");

System.out.println("2. View incomplete tasks");

int choice = readInt("Choose an option: ");

String heading;

Set<Integer> targetIds;

if (choice == 1) {

heading = "=== Completed Tasks ===";

targetIds = completedTaskIds;

} else if (choice == 2) {

heading = "=== Incomplete Tasks ===";

targetIds = incompleteTaskIds;

} else {

System.out.println("Invalid option.");

return;

}

System.out.println(heading);

if (targetIds.isEmpty()) {

System.out.println("No matching tasks found.");

return;

}

for (Integer taskId : targetIds) {

Task task = taskMap.get(taskId);

if (task != null) {

displayTask(task);

}

}

}

// 入队:用 queuedTaskIds 做 O(1) 平均重复检查,避免 LinkedList.contains 的 O(q)。

// Move to queue: use queuedTaskIds for O(1) avg duplicate check, avoiding O(q) contains.

private void moveTaskToQueue() {

int index = chooseTaskIndex();

if (index == -1) {

return;

}

Task task = tasks.get(index);

if (queuedTaskIds.contains(task.getId())) {

System.out.println("That task is already in the processing queue.");

return;

}

processingQueue.offer(task);

queuedTaskIds.add(task.getId());

recordAction(ActionType.MOVE_TO_QUEUE, task, "Moved task into FIFO processing queue.");

System.out.println("Task added to processing queue.");

}

// 查看队列:按队列迭代顺序打印(FIFO)。

// View queue: print in queue iteration order (FIFO).

private void viewProcessingQueue() {

System.out.println("=== Processing Queue ===");

if (processingQueue.isEmpty()) {

System.out.println("The processing queue is empty.");

return;

}

int position = 1;

for (Task task : processingQueue) {

System.out.print(position + ". ");

displayTask(task);

position++;

}

}

// 查看队首:peek 返回但不移除,若空则返回 null。

// View next: peek returns without removing; returns null if empty.

private void viewNextQueueTask() {

Task task = processingQueue.peek();

if (task == null) {

System.out.println("The processing queue is empty.");

return;

}

System.out.println("Next queue task:");

displayTask(task);

}

// 处理队首:poll 移除并返回队首,同时更新 queuedTaskIds。

// Process next: poll removes head and updates queuedTaskIds accordingly.

private void processNextQueueTask() {

Task task = processingQueue.poll();

if (task == null) {

System.out.println("The processing queue is empty.");

return;

}

queuedTaskIds.remove(task.getId());

recordAction(ActionType.PROCESS_QUEUE, task, "Processed the next task from FIFO queue.");

System.out.println("Processed task:");

displayTask(task);

}

// 查看最近动作:使用 Stack.peek() 读取栈顶但不弹出。

// View most recent action: Stack.peek() reads top without removing.

private void viewMostRecentAction() {

if (recentActions.isEmpty()) {

System.out.println("No recent actions recorded.");

return;

}

System.out.println("Most recent action:");

System.out.println(recentActions.peek());

}

// 删除最近动作:Stack.pop() 弹出栈顶动作。

// Remove most recent action: Stack.pop() removes the top action.

private void removeMostRecentActionFromHistory() {

if (recentActions.isEmpty()) {

System.out.println("No recent actions to remove.");

return;

}

ActionRecord removed = recentActions.pop();

System.out.println("Removed action from history:");

System.out.println(removed);

}

// 查看历史:从栈顶到栈底倒序打印(最近在前)。

// View history: iterate stack from top to bottom (most recent first).

private void viewRecentActionHistory() {

System.out.println("=== Recent Action History ===");

if (recentActions.isEmpty()) {

System.out.println("No recent actions recorded.");

return;

}

for (int i = recentActions.size() - 1; i >= 0; i--) {

System.out.println(recentActions.get(i));

}

}

// 记录动作:把 ActionRecord 压入栈中。

// Record action: push an ActionRecord onto the stack.

private void recordAction(ActionType actionType, Task task, String details) {

recentActions.push(new ActionRecord(actionType, task, details));

}

// 设置优先级:更新 Task.priority 后刷新优先队列中的位置。

// Assign priority: update Task.priority then refresh its priority-queue entry if present.

private void assignPriorityToTask() {

int index = chooseTaskIndex();

if (index == -1) {

return;

}

Task task = tasks.get(index);

PriorityLevel oldPriority = task.getPriority();

PriorityLevel newPriority = choosePriorityLevel();

task.setPriority(newPriority);

refreshPriorityQueueEntry(task);

recordAction(

ActionType.ASSIGN_PRIORITY,

task,

"Changed priority from " + oldPriority + " to " + newPriority + ".");

System.out.println("Priority updated.");

displayTask(task, index);

}

// 加入优先队列:用 priorityQueuedTaskIds 做 O(1) 平均重复检查。

// Add to priority queue: use priorityQueuedTaskIds for O(1) avg duplicate check.

private void addTaskToPriorityQueue() {

int index = chooseTaskIndex();

if (index == -1) {

return;

}

Task task = tasks.get(index);

if (priorityQueuedTaskIds.contains(task.getId())) {

System.out.println("That task is already in the priority queue.");

return;

}

priorityQueue.offer(task);

priorityQueuedTaskIds.add(task.getId());

recordAction(ActionType.ADD_TO_PRIORITY_QUEUE, task, "Added task to priority queue.");

System.out.println("Task added to priority queue.");

}

// 查看优先队列:PriorityQueue 迭代不保证有序,因此复制后排序再展示。

// View priority queue: PriorityQueue iteration is not sorted, so copy then sort for display.

private void viewPriorityQueue() {

System.out.println("=== Priority Queue ===");

if (priorityQueue.isEmpty()) {

System.out.println("The priority queue is empty.");

return;

}

List<Task> orderedView = new ArrayList<>(priorityQueue);

orderedView.sort(

Comparator.comparingInt(Task::getPriorityValue).reversed()

.thenComparing(Task::getTitle, String.CASE_INSENSITIVE_ORDER));

int position = 1;

for (Task task : orderedView) {

System.out.print(position + ". ");

displayTask(task);

position++;

}

}

// 查看最高优先级:peek 获取但不移除(若空返回 null)。

// View highest priority: peek returns without removal (null if empty).

private void viewHighestPriorityTask() {

Task task = priorityQueue.peek();

if (task == null) {

System.out.println("The priority queue is empty.");

return;

}

System.out.println("Highest-priority task:");

displayTask(task);

}

// 处理最高优先级:poll 移除堆顶,并同步更新 priorityQueuedTaskIds。

// Process highest priority: poll removes heap head and updates priorityQueuedTaskIds.

private void processHighestPriorityTask() {

Task task = priorityQueue.poll();

if (task == null) {

System.out.println("The priority queue is empty.");

return;

}

priorityQueuedTaskIds.remove(task.getId());

recordAction(ActionType.PROCESS_PRIORITY_QUEUE, task, "Processed the highest-priority task.");

System.out.println("Processed highest-priority task:");

displayTask(task);

}

// 添加标签:若 task.addTag(tag) 成功,增量更新 allTags/tagFrequency/tasksByTag。

// Add tag: if task.addTag(tag) succeeds, incrementally update all tag indexes.

private void addTagToTask() {

int index = chooseTaskIndex();

if (index == -1) {

return;

}

Task task = tasks.get(index);

String tag = readTag("Enter a tag: ");

if (task.addTag(tag)) {

addTagIndexes(task, tag);

recordAction(ActionType.ADD_TAG, task, "Added tag '" + tag + "'.");

System.out.println("Tag added.");

} else {

System.out.println("That task already has the tag '" + tag + "'.");

}

displayTask(task, index);

}

// 检查标签成员:task.hasTag(tag) 基于 HashSet,平均 O(1)。

// Check membership: task.hasTag(tag) uses HashSet, O(1) average.

private void checkTaskTagMembership() {

int index = chooseTaskIndex();

if (index == -1) {

return;

}

Task task = tasks.get(index);

String tag = readTag("Enter a tag to check: ");

if (task.hasTag(tag)) {

System.out.println("Task " + task.getId() + " contains the tag '" + tag + "'.");

} else {

System.out.println("Task " + task.getId() + " does not contain the tag '" + tag + "'.");

}

}

// 查看所有标签:直接遍历 allTags(增量维护),无需全量重建。

// View all tags: iterate allTags (incrementally maintained), no full rebuild needed.

private void viewAllTags() {

System.out.println("=== All Unique Tags ===");

if (allTags.isEmpty()) {

System.out.println("No tags available.");

return;

}

int position = 1;

for (String tag : allTags) {

System.out.println(position + ". " + tag);

position++;

}

}

// 按标签过滤:用 tasksByTag 直接得到匹配ID集合,平均 O(1+k)。

// Filter by tag: tasksByTag gives matching IDs directly, O(1+k) average.

private void filterTasksByTag() {

if (allTags.isEmpty()) {

System.out.println("No tags available.");

return;

}

String tag = readTag("Enter a tag to filter by: ");

Set<Integer> matchingTaskIds = tasksByTag.get(tag);

System.out.println("=== Tasks Tagged '" + tag + "' ===");

if (matchingTaskIds == null || matchingTaskIds.isEmpty()) {

System.out.println("No tasks found with that tag.");

return;

}

for (Integer taskId : matchingTaskIds) {

Task task = taskMap.get(taskId);

if (task != null) {

displayTask(task);

}

}

}

// 按ID查找:taskMap.get(id) 平均 O(1),不用扫描列表。

// Lookup by ID: taskMap.get(id) is O(1) average, avoids list scans.

private void lookupTaskById() {

if (taskMap.isEmpty()) {

System.out.println("No tasks available.");

return;

}

int id = readInt("Enter task ID: ");

Task task = taskMap.get(id);

if (task == null) {

System.out.println("No task found with ID " + id + ".");

return;

}

System.out.println("Task found:");

displayTask(task);

}

// 查看标签频率:遍历 tagFrequency 输出(O(u))。

// View tag frequency: iterate tagFrequency entries (O(u)).

private void viewTagFrequency() {

System.out.println("=== Tag Frequency ===");

if (tagFrequency.isEmpty()) {

System.out.println("No tags available.");

return;

}

for (Map.Entry<String, Integer> entry : tagFrequency.entrySet()) {

System.out.println(entry.getKey() + " -> " + entry.getValue());

}

}

// 最频繁标签:扫描 tagFrequency 找最大值(O(u)),平衡实现简单与性能。

// Most frequent tag: scan tagFrequency to find max (O(u)), simple vs performance tradeoff.

private void viewMostFrequentTag() {

if (tagFrequency.isEmpty()) {

System.out.println("No tags available.");

return;

}

System.out.println("Most frequent tag: " + getMostFrequentTagSummary());

}

// 排序标签:按需创建 TreeSet(O(u log u))展示排序视图。

// Sorted tags: build TreeSet on demand (O(u log u)) for sorted view.

private void viewSortedTags() {

System.out.println("=== Sorted Tags (TreeSet) ===");

if (allTags.isEmpty()) {

System.out.println("No tags available.");

return;

}

TreeSet<String> sortedTags = new TreeSet<>(allTags);

int position = 1;

for (String tag : sortedTags) {

System.out.println(position + ". " + tag);

position++;

}

}

// 排序频率:按需创建 TreeMap(按 key 排序)展示排序视图。

// Sorted frequencies: build TreeMap on demand (sorted by key) for display.

private void viewSortedTagFrequencies() {

System.out.println("=== Sorted Tag Frequency (TreeMap) ===");

if (tagFrequency.isEmpty()) {

System.out.println("No tags available.");

return;

}

TreeMap<String, Integer> sortedTagFrequency = new TreeMap<>(tagFrequency);

for (Map.Entry<String, Integer> entry : sortedTagFrequency.entrySet()) {

System.out.println(entry.getKey() + " -> " + entry.getValue());

}

}

// Hash vs Tree 对比:Hash 结构无序,Tree 结构排序;LinkedHashSet 保留插入序。

// Hash vs Tree: Hash is unordered, Tree is sorted; LinkedHashSet preserves insertion order.

private void compareTagViews() {

System.out.println("=== Hash vs Tree Comparison ===");

if (allTags.isEmpty()) {

System.out.println("No tags available.");

return;

}

System.out.println("allTags (insertion order): " + allTags);

System.out.println("TreeSet sortedTags (sorted): " + new TreeSet<>(allTags));

System.out.println("tagFrequency (hash map): " + tagFrequency);

System.out.println("TreeMap sortedTagFrequency (sorted): " + new TreeMap<>(tagFrequency));

}

// 系统概览:展示关键计数与常见视图,便于检查索引一致性。

// System overview: prints counts and views to help verify index consistency.

private void viewSystemOverview() {

System.out.println("=== System Overview ===");

System.out.println("Task count: " + tasks.size());

System.out.println("Task map size: " + taskMap.size());

System.out.println("Queue size: " + processingQueue.size());

System.out.println("Recent action count: " + recentActions.size());

System.out.println("Priority queue size: " + priorityQueue.size());

System.out.println("Unique tag count: " + allTags.size());

System.out.println("Completed tasks: " + completedTaskIds.size());

System.out.println("Incomplete tasks: " + incompleteTaskIds.size());

if (!allTags.isEmpty()) {

System.out.println("Sorted tags: " + new TreeSet<>(allTags));

System.out.println("Most frequent tag: " + getMostFrequentTagSummary());

}

if (!tasks.isEmpty()) {

System.out.println();

System.out.println("Main task list:");

viewAllTasks();

}

if (!processingQueue.isEmpty()) {

System.out.println();

System.out.println("FIFO queue:");

viewProcessingQueue();

}

if (!priorityQueue.isEmpty()) {

System.out.println();

System.out.println("Priority queue:");

viewPriorityQueue();

}

if (!tagFrequency.isEmpty()) {

System.out.println();

System.out.println("Sorted tag frequencies:");

viewSortedTagFrequencies();

}

if (!recentActions.isEmpty()) {

System.out.println();

System.out.println("Most recent activities (up to 10):");

int shown = 0;

for (int i = recentActions.size() - 1; i >= 0 && shown < 10; i--) {

System.out.println(recentActions.get(i));

shown++;

}

}

}

// 创建优先队列:比较器按 priorityValue 降序,再按标题不区分大小写。

// Create priority queue: comparator sorts by priorityValue desc then title case-insensitive.

private PriorityQueue<Task> createPriorityQueue() {

return new PriorityQueue<>(

Comparator.comparingInt(Task::getPriorityValue).reversed()

.thenComparing(Task::getTitle, String.CASE_INSENSITIVE_ORDER));

}

// 刷新优先队列条目:若任务已在队列中,则 remove+offer 以维护堆性质。

// Refresh PQ entry: if task is in PQ, remove+offer to restore heap order after changes.

private void refreshPriorityQueueEntry(Task task) {

if (priorityQueuedTaskIds.contains(task.getId()) && priorityQueue.remove(task)) {

priorityQueue.offer(task);

}

}

// 增量更新标签索引:更新 allTags、tagFrequency、tasksByTag 三者保持一致。

// Incremental tag index update: update allTags, tagFrequency, tasksByTag consistently.

private void addTagIndexes(Task task, String tag) {

allTags.add(tag);

tagFrequency.put(tag, tagFrequency.getOrDefault(tag, 0) + 1);

tasksByTag.computeIfAbsent(tag, ignored -> new LinkedHashSet<>()).add(task.getId());

}

// 删除任务索引:移除 taskMap、状态集合,并对该任务所有标签做反向更新。

// Remove task indexes: remove from taskMap/status sets and reverse-update all tag indexes.

private void removeTaskFromIndexes(Task task) {

taskMap.remove(task.getId());

completedTaskIds.remove(task.getId());

incompleteTaskIds.remove(task.getId());

for (String tag : task.getTags()) {

removeTagIndexes(task, tag);

}

}

// 反向更新标签索引:从 tasksByTag 删除ID,减少频率并在归零时清理 tag。

// Reverse tag index update: remove ID from tasksByTag, decrement frequency, cleanup if count hits 0.

private void removeTagIndexes(Task task, String tag) {

Set<Integer> taggedTaskIds = tasksByTag.get(tag);

if (taggedTaskIds != null) {

taggedTaskIds.remove(task.getId());

if (taggedTaskIds.isEmpty()) {

tasksByTag.remove(tag);

}

}

Integer currentCount = tagFrequency.get(tag);

if (currentCount == null) {

return;

}

if (currentCount <= 1) {

tagFrequency.remove(tag);

allTags.remove(tag);

} else {

tagFrequency.put(tag, currentCount - 1);

}

}

// 最频繁标签摘要:扫描频率表,若并列则按字母序选择更小的 tag。

// Most frequent tag summary: scan frequency map; tie-break by alphabetical order.

private String getMostFrequentTagSummary() {

String mostFrequentTag = null;

int highestCount = 0;

for (Map.Entry<String, Integer> entry : tagFrequency.entrySet()) {

String candidateTag = entry.getKey();

int candidateCount = entry.getValue();

if (mostFrequentTag == null

|| candidateCount > highestCount

|| (candidateCount == highestCount && candidateTag.compareTo(mostFrequentTag) < 0)) {

mostFrequentTag = candidateTag;

highestCount = candidateCount;

}

}

return mostFrequentTag + " (" + highestCount + ")";

}

// 读取整数:循环直到输入可解析为 int,避免程序崩溃。

// Read integer: loop until a valid int is parsed to prevent crashes.

private int readInt(String prompt) {

while (true) {

System.out.print(prompt);

String line = input.nextLine().trim();

try {

return Integer.parseInt(line);

} catch (NumberFormatException ex) {

System.out.println("Please enter a valid integer.");

}

}

}

// 读取非空行:用于标题等必须输入的字段。

// Read non-empty line: used for required fields like task title.

private String readNonEmptyLine(String prompt) {

while (true) {

System.out.print(prompt);

String line = input.nextLine().trim();

if (!line.isEmpty()) {

return line;

}

System.out.println("Input cannot be empty.");

}

}

// 读取标签:统一转小写实现规范化(normalization)。

// Read tag: normalize to lower-case for consistent matching.

private String readTag(String prompt) {

return readNonEmptyLine(prompt).toLowerCase();

}

// 显示任务(带索引):用于 tasks 列表显示。

// Display task (with index): used for list-based display.

private void displayTask(Task task, int index) {

System.out.printf(

"%d id=%d | %s | completed=%s | priority=%s | tags=%s%n",

index,

task.getId(),

task.getTitle(),

task.isCompleted(),

task.getPriority(),

formatTags(task));

}

// 显示任务(不带索引):用于队列/Map/索引查询结果显示。

// Display task (no index): used for queue/map/index-based outputs.

private void displayTask(Task task) {

System.out.printf(

"id=%d | %s | completed=%s | priority=%s | tags=%s%n",

task.getId(),

task.getTitle(),

task.isCompleted(),

task.getPriority(),

formatTags(task));

}

// 标签格式化:用 TreeSet 排序显示,方便阅读但不改变存储结构。

// Tag formatting: sort with TreeSet for readability without changing storage.

private String formatTags(Task task) {

if (task.getTags().isEmpty()) {

return "\[\]";

}

return new TreeSet<>(task.getTags()).toString();

}

// 选择任务索引:先显示所有任务再读入索引并校验范围。

// Choose task index: show tasks then read index and validate range.

private int chooseTaskIndex() {

if (tasks.isEmpty()) {

System.out.println("No tasks available.");

return -1;

}

viewAllTasks();

int index = readInt("Enter task index: ");

if (index < 0 || index >= tasks.size()) {

System.out.println("Invalid task index.");

return -1;

}

return index;

}

// 选择优先级:把用户输入映射到枚举 PriorityLevel。

// Choose priority: map user input to PriorityLevel enum.

private PriorityLevel choosePriorityLevel() {

System.out.println("1. HIGH");

System.out.println("2. MEDIUM");

System.out.println("3. LOW");

int choice = readInt("Choose a priority: ");

switch (choice) {

case 1:

return PriorityLevel.HIGH;

case 2:

return PriorityLevel.MEDIUM;

case 3:

return PriorityLevel.LOW;

default:

System.out.println("Invalid choice. Defaulting to LOW.");

return PriorityLevel.LOW;

}

}

// 动作类型枚举:用于 ActionRecord 标注操作类别。

// Action type enum: used by ActionRecord to label operations.

private enum ActionType {

ADD,

EDIT,

REMOVE,

COMPLETE,

MOVE_TO_QUEUE,

PROCESS_QUEUE,

ASSIGN_PRIORITY,

ADD_TO_PRIORITY_QUEUE,

PROCESS_PRIORITY_QUEUE,

ADD_TAG

}

// 优先级枚举:包含数值用于比较(HIGH=3, MEDIUM=2, LOW=1)。

// Priority enum: numeric values used for comparison (HIGH=3, MEDIUM=2, LOW=1).

private enum PriorityLevel {

HIGH(3),

MEDIUM(2),

LOW(1);

private final int value;

PriorityLevel(int value) {

this.value = value;

}

public int getValue() {

return value;

}

}

// 任务类:包含 id/title/completed/priority/tags(tags 用 Set 防重复)。

// Task model: includes id/title/completed/priority/tags (Set prevents duplicates).

private static class Task {

private final int id;

private String title;

private boolean completed;

private PriorityLevel priority;

private final Set<String> tags = new HashSet<>();

public Task(int id, String title) {

this.id = id;

this.title = title;

this.completed = false;

this.priority = PriorityLevel.LOW;

}

public int getId() {

return id;

}

public String getTitle() {

return title;

}

public void setTitle(String title) {

this.title = title;

}

public boolean isCompleted() {

return completed;

}

public void setCompleted(boolean completed) {

this.completed = completed;

}

public PriorityLevel getPriority() {

return priority;

}

public void setPriority(PriorityLevel priority) {

this.priority = priority;

}

public int getPriorityValue() {

return priority.getValue();

}

public boolean addTag(String tag) {

return tags.add(tag);

}

public boolean hasTag(String tag) {

return tags.contains(tag);

}

public Set<String> getTags() {

return tags;

}

@Override

public String toString() {

return "id="

  • id

  • " | "

  • title

  • " | completed="

  • completed

  • " | priority="

  • priority

  • " | tags="

  • new TreeSet<>(tags);

}

}

// 动作记录类:记录动作类型、目标任务与细节文本。

// Action record: stores action type, target task, and detail message.

private static class ActionRecord {

private final ActionType actionType;

private final Task task;

private final String details;

public ActionRecord(ActionType actionType, Task task, String details) {

this.actionType = actionType;

this.task = task;

this.details = details;

}

@Override

public String toString() {

String taskSummary = task == null ? "n/a" : ("id=" + task.getId() + ", title=" + task.getTitle());

return actionType + " | task=" + taskSummary + " | details=" + details;

}

}

}