这个part都是关于mutable和immutable的问题。
目录:
- I. 认识mutability —— mutable和immutable的区别是啥,用 mutable object有啥好处
- II. mutability的风险 —— 赋值和iterate过程中,可能造成的bug
- III. 使用immutable的建议——用immutability来提高correctness, clarity, & changeability
I. 认识mutability
1. 什么是mutability
字面上,mutable object是可以改变值的(比如StringBuilding
),而immutable则创建后总是这个值(比如String
)。接下来可以看看,内部的情况。
// case 1: immutable
String s = "a";
s = s + "b";
// case 2: mutable
StringBuilder sb = new StringBuilder("a");
sb.append("b");
2. mutable和immutable怎么运行的
// t 指向 s, 其实是创建了一个新的物件
// t 的值改变,并不会影响到 s
// 此时 s 为 "ab",t 为 "abc"
String t = s;
t = t + "c";
// tb 指向 sb,则 tb 和 sb 指向同一个值
// 当 tb 变动,sb 也会随之变动
// 此时,tb 和 sb 均为 "abc"
StringBuilder tb = sb;
tb.append("c");

3. mutable data的好处
好处:
- mutable objects 起到优化作用。
- program之间可以共享同一个mutable data。
如下例子👇🌰:
// 因为String是immutable
// 所以本质上说,这个for循环不断创建了n个新的string
String s = "";
for (int i = 0; i < n; ++i) {
s = s + i;
}
// 因而,如果使用 StringBuilder,通过改变internal data
// 可以避免这种自我复制
StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; ++i) {
sb.append(String.valueOf(i));
}
String s = sb.toString();
II. mutability的风险
1. 赋值过程中造成的风险
immutable types有它自己的好处:
- safer from bugs
- easier to understand
- read for change
mutable的风险有二:
- 通常是赋值造成的风险。如果data被不同method运算过后,可能会出现意象不到的错误,因为它内部的值被改变过。
- 返回值是mutable,不同program之间,因为precondition和内部运算的问题,造成混乱。
解决的方法,可以是返回一个copy,但是new的意义是使object更容易share to client,而不是变成一个immutable object:
// 这种new一下,被称为defensive copying
return new Date(groundhogAnser.getTime()); // Date() 假定是我新建的method
所以:(Warning❗) 通常mutatable input不够简洁,所以不能用。
2. Iterator过程中造成的风险
2.1. iterator的构成:
List<String> list = ...;
Iterator iter = list.iterator();
while (iter.hasNext()) {
String str = iter.next();
System.out.println(str);
}
next( ) 由两个步骤构成,1. return出list的元素 2. 切到下一个元素。
2.2 为啥要有iterate:
因为有很多不同的data structure,比如linked lists, maps, hash tables, 可是iterator用一种统一的形式,可以取得data,这方便了大家使用method,不用针对不同data,用不同的calling。这使得代码也更加简单。
2.3 iterator造成的bug
public static void dropCourse6(ArrayList<String> subjects) {
MyIterator iter = new MyIterator(subjects);
while (iter.hasNext()) {
String subject = iter.next();
if (subject.startsWith("6.")) {
subjects.remove(subject); // 修正bug 改成 iter.remove();
}
}
}
// 运行 dropCourse6(["6.045", "6.005", "6.813"])
// 希望输出[], 但实际输出了 ["6.005"]
画一张snapshot diagram,其实能看出,index的问题,导致了bug。

当next( ),让index变成1时候,从原来 ["6.045", "6.005", "6.813"] 变成了 ["6.005", "6.813"],此时List的变化,导致了错误。而修正bug的方法也很简单,从subject.remove变成iter.remove,因为iter已经知道那个元素的位置了。但是,注意❗❗❗如果其他iterator同时出现,那么就会出现问题。
III. 使用immutable的建议
-
The primitive types and primitive wrappers are all immutable. If you need to compute with large numbers,
[BigInteger](http://docs.oracle.com/javase/8/docs/api/?java/math/BigInteger.html)
and[BigDecimal](http://docs.oracle.com/javase/8/docs/api/?java/math/BigDecimal.html)
are immutable. -
Don't use mutable
Date
s but instead use the appropriate immutable type from[java.time](http://docs.oracle.com/javase/8/docs/api/index.html?java/time/package-summary.html)
based on the granularity of timekeeping you need. -
The usual implementations of Java's collections types —
List
,Set
,Map
— are all mutable:ArrayList
,HashMap
, etc. The[Collections](http://docs.oracle.com/javase/8/docs/api/?java/util/Collections.html)
utility class has methods for obtaining unmodifiable views of these mutable collections:- [Collections.unmodifiableList] (https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#unmodifiableList-java.util.List-)
- Collections.unmodifiableSet
- Collections.unmodifiableMap
You can think of the unmodifiable view as a wrapper around the underlying list/set/map. A client who has a reference to the wrapper and tries to perform mutations —
add
,remove
,put
, etc. — will trigger an[UnsupportedOperationException](http://docs.oracle.com/javase/8/docs/api/?java/lang/UnsupportedOperationException.html)
.Before we pass a mutable collection to another part of our program, we can wrap it in an unmodifiable wrapper. We should be careful at that point to forget our reference to the mutable collection, lest we accidentally mutate it. (One way to do that is to let it go out of scope.) Just as a mutable object behind a
final
reference can be mutated, the mutable collection inside an unmodifiable wrapper can still be modified by someone with a reference to it, defeating the wrapper. -
Collections
also provides methods for obtaining immutable empty collections:[Collections.emptyList](https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#emptyList--)
, etc. Nothing's worse than discovering your definitely very empty list is suddenly definitely not empty!
中间忙了一下荷兰的申请,导致进度上拖到了。
因为我忘记之前在上什么...所以把东西拆成A和B部分。
Ronn Liu 🙋♂️
2019/01/07 - 2019/01/013 📌
网友评论