Set 数据类型
除了以前学的 List,Java 中还支持Set。
Set 不支持索引,但是你可以确定某个元素是否在 set 中
1 | Set<String> s = new HashSet<>(); |
Exceptions
如果你的代码有哪些操作是不能做的,你可以选择 throw 一个 exception,这会在报错信息中显示,例如:
1 | public void add(T x) { |
让数据类型支持 for 循环迭代 & Iterators
在课堂上我们创建了一个 ArraySet,但目前无法使用 for 循环对其进行遍历,因此需要引入 iterator 这一机制。
要使一个类支持 for 循环,需要满足以下几点:
- 在该类中实现
iterator()方法,并让该方法返回Iterator<T>类型 - 实现一个
Iterator<T>类型的类,该类必须实现hasNext()和next()方法 - 该类需要
implements Iterable<T>
考虑以下完整的可迭代 ArraySet 实现:
1 | import java.util.Iterator; |
这里的 ArraySetIterator 可以理解为一个“巫师”,用于顺序访问 ArraySet 中的元素:
- 初始位置
wizPos = 0 - 调用
hasNext()用于判断是否还有元素 - 调用
next()时:- 返回当前位置的元素
- 然后将指针移动到下一个位置

上图展示了迭代的内部过程。本质上,for-each 循环只是对 Iterator 的一种语法封装。
如果 ArraySet<T> 不 implements Iterable<T>,则无法使用 for 循环,只能手动编写类似上图中的迭代代码。这种支持被称为 iterable。
Iterator 和 Iterable 的区别
- Iterator 是执行遍历的游标对象
- Iterable 则是能够生成一个 Iterator、从而实现对自身内容进行遍历的对象。
- 例如:一个本身不能被遍历的对象,通过生成一个 iterator 就可以实现遍历
打印数据类型里的内容 & toString
当调用 System.out.println(x) 时,Java 会自动调用 x.toString() 方法。
像 ArrayList 这样的类都已经重写了 toString(),因此可以直接 println()。而我们之前实现的 ArraySet 如果直接使用 println(),会打印对象的内存地址(类似 ArraySet@1a2b3c)。因此需要在类中重写该方法:
1 |
|
StringBuilder 类用于创建和操作可变字符串。与 String 不同,StringBuilder 可以在原对象上进行修改,而不会产生新的对象。
这里使用 StringBuilder 而不是 String 的原因是 String 每次拼接都会创建新对象,时间复杂度高
因此常见模式是:用 StringBuilder 构建,最后调用 toString() 转换为 String
判断数据类型里的内容是否相等 & equals
== 和 .equals() 在 Java 中的行为完全不同:
==比较的是两个变量是否指向同一个对象(内存地址)- 即使两个 Array 内容完全相同,只要不是同一个对象,也会返回
false
- 即使两个 Array 内容完全相同,只要不是同一个对象,也会返回
.equals(Object o)是定义在Object类中的方法,很多类会重写它,让他比较内容是否相等(逻辑相等)
在这里引入 instanceof 这样一个新语法:
1 |
|
1 | o instanceof Dog uddaDog |
上面这行代码实际上做了三件事:
- 判断
o的动态类型是否是Dog(或其子类) - 如果是,则自动完成类型转换,并将其赋值给静态类型为
Dog的uddaDog变量 - 如果
o是 null,则instanceof会直接返回false,不会报错
额外:手动实现 instanceof 的功能
这部分最新版课堂没交,却在 Project1 中的 equals 方法中用到了,我们要手动实现 instanceof 的功能。
1 | public boolean equals(Object other) { |