CS61B 课程笔记(Disc 06 Selecting ADTs)
CS61B 课程笔记(Disc 06 Selecting ADTs)
1. 不可变对象 (Immutable Objects)
访问控制
访问控制允许我们限制字段、方法和类的使用:
- public:任何人都可以访问。
- protected:类自身、包内和任何子类可访问。
- default(无修饰符):类自身和包内可访问。
- private:仅类自身可访问。
1.1 不可变类
一个类是不可变的,如果其实例在构造后不能改变。判断以下类是否不可变:
Pebble 类:
1
2
3
4public class Pebble {
public int weight;
public Pebble() { weight = 1; }
}- 结论:这是一个可变类。
Pebble
的weight
字段是公共的,因此状态可以轻易改变。
- 结论:这是一个可变类。
Rock 类:
1
2
3
4public class Rock {
public final int weight;
public Rock(int w) { weight = w; }
}- 结论:这是一个不可变类。
Rock
的weight
字段是final
,因此一旦初始化后不能被重新赋值。
- 结论:这是一个不可变类。
Rocks 类:
1
2
3
4public class Rocks {
public final Rock[] rocks;
public Rocks(Rock[] rox) { rocks = rox; }
}- 结论:尽管
rocks
不能被重新赋值,但数组中的内容可以被改变,因此Rocks
是可变的。
- 结论:尽管
SecretRocks 类:
1
2
3
4public class SecretRocks {
private Rock[] rocks;
public SecretRocks(Rock[] rox) { rocks = rox; }
}- 结论:
rocks
变量是私有的,外部不能重新赋值或修改其元素。然而,rox
在传入时可以被外部编辑,因此SecretRocks
在技术上是可变的。可以通过使用Arrays.copyOf
使其不可变。
- 结论:
2. 选择抽象数据类型 (Selecting ADTs)
2.1 PunkRock 类
1 | public class PunkRock { |
- 结论:通过
public myBand()
方法可以访问和修改PunkRock
的私有数组内容,因此这个类是可变的。
2.2 MommaRock 类
1 | public class MommaRock { |
- 结论:这是一个可变类,因为
Pebble
有公共变量可以被改变。例如,mr.baby.weight = 5
。
3. 打破系统 (Breaking the System)
3.1 不良整数栈 (BadIntegerStack)
下面是一个不良实现的栈 ADT:
1 | public class BadIntegerStack { |
(a) 利用缺陷
填写以下main
方法以打印“Success”并导致BadIntegerStack
产生NullPointerException
:
1 | public static void main(String[] args) { |
(b) 利用另一个缺陷
完成以下main
方法,使栈看起来无限高:
1 | public static void main(String[] args) { |
(c) 改进建议
为了防止BadIntegerStack
抛出NullPointerException
或产生无限栈,可以将top
设为私有(private)。
4. 设计停车场 (Design a Parking Lot)
设计一个停车场包,智能地分配停车位给汽车。确定所需的类并设计每个类的 API。
停车场需求:
- 停车位可以是普通、紧凑或残疾专用。
- 新车到达时,系统应根据汽车类型分配特定空间。
- 所有汽车均可停放在普通停车位,因此紧凑型车可停放在紧凑型和普通停车位。
- 当汽车离开时,系统应记录该停车位为空闲状态。
- 包应设计成允许不同停车场有不同数量的三种类型停车位。
- 停车位应考虑与入口的距离,尽量将汽车停放在离入口最近的位置。
类设计:
Car 类:
public Car(boolean isCompact, boolean isHandicapped)
:创建具有给定大小和权限的汽车。public boolean isCompact()
:返回汽车是否可以适应紧凑停车位。public boolean isHandicapped()
:返回汽车是否可以停在残疾专用停车位。public boolean findSpotAndPark(ParkingLot lot)
:尝试在停车场停放此汽车,成功返回true
。public void leaveSpot()
:腾出此汽车的停车位。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40public class Car {
private boolean isCompact; // 是否为紧凑型车
private boolean isHandicapped; // 是否为残疾专用车
private Spot currentSpot; // 当前占用的停车位
// 构造函数:创建具有给定大小和权限的汽车
public Car(boolean isCompact, boolean isHandicapped) {
this.isCompact = isCompact;
this.isHandicapped = isHandicapped;
}
// 返回汽车是否为紧凑型车
public boolean isCompact() {
return isCompact;
}
// 返回汽车是否为残疾专用车
public boolean isHandicapped() {
return isHandicapped;
}
// 尝试在停车场停放此汽车,成功返回true
public boolean findSpotAndPark(ParkingLot lot) {
return lot.findSpotAndPark(this);
}
// 腾出此汽车的停车位
public void leaveSpot() {
if (currentSpot != null) {
currentSpot.vacate(); // 腾出停车位
currentSpot = null; // 重置当前停车位
}
}
// 设置当前占用的停车位
void setCurrentSpot(Spot spot) {
this.currentSpot = spot;
}
}Spot 类(私有):
private Spot(String type, int proximity)
:创建特定类型和接近度的停车位。private boolean isHandicapped()
:返回此停车位是否为残疾司机保留。private boolean isCompact()
:返回此停车位是否仅能容纳紧凑型车。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43private class Spot {
private String type; // 停车位类型: "handicapped", "compact", "regular"
private int proximity; // 停车位距离入口的距离
private boolean isOccupied; // 停车位是否被占用
// 构造函数:创建特定类型和接近度的停车位
private Spot(String type, int proximity) {
this.type = type;
this.proximity = proximity;
this.isOccupied = false; // 初始化为空闲状态
}
// 返回此停车位是否为残疾司机保留
private boolean isHandicapped() {
return "handicapped".equals(type);
}
// 返回此停车位是否仅能容纳紧凑型车
private boolean isCompact() {
return "compact".equals(type);
}
// 返回停车位是否被占用
public boolean isOccupied() {
return isOccupied;
}
// 停放车辆
public void park() {
isOccupied = true; // 标记为已占用
}
// 腾出停车位
public void vacate() {
isOccupied = false; // 标记为可用
}
// 获取停车位的接近度
public int getProximity() {
return proximity;
}
}ParkingLot 类:
public ParkingLot(int[] handicappedDistances, int[] compactDistances, int[] regularDistances)
:创建停车场,包含指定数量的残疾停车位、紧凑停车位和普通停车位。public boolean findSpotAndPark(Car toPark)
:尝试为给定汽车找到停车位并停放。若无可用停车位则返回false
。public void removeCarFromSpot(Car toRemove)
:记录停车位被腾出,并使停车位再次可用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75import java.util.*;
public class ParkingLot {
private PriorityQueue<Spot> handicappedSpots; // 残疾停车位的优先队列
private PriorityQueue<Spot> compactSpots; // 紧凑停车位的优先队列
private PriorityQueue<Spot> regularSpots; // 普通停车位的优先队列
private Map<Car, Spot> occupiedSpots; // 记录每辆车和其占用的停车位
// 构造函数:创建停车场,包含指定数量的三种类型停车位
public ParkingLot(int[] handicappedDistances, int[] compactDistances, int[] regularDistances) {
// 初始化优先队列,根据停车位的接近度进行排序
handicappedSpots = new PriorityQueue<>(Comparator.comparingInt(Spot::getProximity));
compactSpots = new PriorityQueue<>(Comparator.comparingInt(Spot::getProximity));
regularSpots = new PriorityQueue<>(Comparator.comparingInt(Spot::getProximity));
occupiedSpots = new HashMap<>(); // 初始化已占用停车位的记录
// 将残疾停车位添加到优先队列
for (int distance : handicappedDistances) {
handicappedSpots.offer(new Spot("handicapped", distance));
}
// 将紧凑停车位添加到优先队列
for (int distance : compactDistances) {
compactSpots.offer(new Spot("compact", distance));
}
// 将普通停车位添加到优先队列
for (int distance : regularDistances) {
regularSpots.offer(new Spot("regular", distance));
}
}
// 尝试为给定汽车找到停车位并停放
public boolean findSpotAndPark(Car toPark) {
Spot spot = null; // 初始化停车位
// 根据汽车类型寻找合适的停车位
if (toPark.isHandicapped() && !handicappedSpots.isEmpty()) {
spot = handicappedSpots.poll(); // 从残疾停车位队列中取出一个停车位
} else if (toPark.isCompact() && !compactSpots.isEmpty()) {
spot = compactSpots.poll(); // 从紧凑停车位队列中取出一个停车位
} else if (!regularSpots.isEmpty()) {
spot = regularSpots.poll(); // 从普通停车位队列中取出一个停车位
}
// 如果找到停车位,则进行停放
if (spot != null) {
spot.park(); // 停放车辆
occupiedSpots.put(toPark, spot); // 记录车辆和其占用的停车位
toPark.setCurrentSpot(spot); // 设置当前停车位
return true; // 停放成功
}
return false; // 没有可用停车位
}
// 记录停车位被腾出,并使停车位再次可用
public void removeCarFromSpot(Car toRemove) {
Spot spot = occupiedSpots.remove(toRemove); // 移除记录并获取停车位
if (spot != null) {
spot.vacate(); // 腾出停车位
// 将空闲的停车位重新加入相应的优先队列
switch (spot.type) {
case "handicapped":
handicappedSpots.offer(spot); // 重新加入残疾停车位队列
break;
case "compact":
compactSpots.offer(spot); // 重新加入紧凑停车位队列
break;
case "regular":
regularSpots.offer(spot); // 重新加入普通停车位队列
break;
}
}
toRemove.leaveSpot(); // 腾出汽车的停车位
}
}
数据结构选择:
优先队列:使用优先队列来管理停车位,以便根据距离入口的接近度进行停车位的选择。
Map<Car,
Spot>:通过Map
跟踪每辆车和它所占用的停车位,以便在车辆离开时能够快速找到并释放停车位。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Totoroの旅!
评论