最近在软件构造课上学习了Java中的两种数据类型:mutable(可变的)和immutable(不可变的)。有时候感觉有些绕,在这里记录一下我对这两种类型的理解。
immutable(不可变的):一旦创建,就不能更改它的值/引用。
mutable(可变的):创建之后,该对象拥有可以更改其值/引用的方法,可以利用这些方法改变。
需要注意的一点是:
改变一个变量的引用:将该变量指向另一个值的存储空间
改变一个变量内的值:将该变量当前指向的值的存储空间中写入一个新的值。
String类型是immutable的,一旦创建,不能更改它指向的值,只能改变它的引用
String s = "a";
s = s.concat("b"); // s+="b" and s=s+"b" also mean the same thing
这段代码的内存,用内存图的方式可以画成这样:
也就是说,如果初始化了一个String类型的对象,再去给它加上一段字符,则会更改它指向的地址,而不是更改地址里的值。
StringBuilder类型是mutable的,创建成功后,可以利用一些方法来改变它指向地址的值。
StringBuilder sb = new StringBuilder("a");
sb.append("b");
这段代码的内存图表示为:
可以发现,sb指向的内存地址没变,只是地址内的内容改变了。
虽然看起来,两种方式并没有什么区别,但是,参考如下代码:
String t = s;
t = t + "c";
StringBuilder tb = sb;
tb.append("c");
可以发现,当有变量t
指向s
,tb
指向sb
时,问题就呈现出来了,当改变t
的值时,s
的值并没有改变;但当改变tb
的值时,sb
的值跟着改变,这就会造成一些可怕的后果。
大家可能会想,既然可变类型会产生一些意外的后果,而且我们已经有了String类型,那么要StringBuilder类型做什么呢?参考下面代码:
String s = "";
for (int i = 0; i < n; ++i) {
s = s + i;
}
如果s被设为String类型,那么,每次在s后面接字符串的时候,都会将之前s中的内容复制一份再加上n,然后放到另一块内存区域,由于要更改s的指向,所以每次都需要复制之前的字符串,“0”被复制了n次,这样本来增加了n个字符,结果却需要O(n^2);
然而使用StringBuilder就可以直接在原字符串后增加,效率提高了很多,当需要转成字符串时,只需利用toString()方法。
StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; ++i) {
sb.append(String.valueOf(i));
}
String s = sb.toString();
首先写一个sum函数,计算参数列表中元素之和:
/** @return the sum of the numbers in the list */
public static int sum(List list) {
int sum = 0;
for (int x : list)
sum += x;
return sum;
}
然后再写一个计算列表元素绝对值之和:
/** @return the sum of the absolute values of the numbers in the list */
public static int sumAbsolute(List list) {
// let's reuse sum(), because DRY, so first we take absolute values
for (int i = 0; i < list.size(); ++i)
list.set(i, Math.abs(list.get(i)));
return sum(list);
}
然后查看下面函数的输出:
public static void main(String[] args) {
// ...
List myData = Arrays.asList(-5, -3, -2);
System.out.println(sumAbsolute(myData));
System.out.println(sum(myData));
}
会发现,输出的都是10,而不是我们预期的10的-10,可见,可变数据类型发生了参数的更改,这在我们写程序的时候会发生致命的问题。
另外,如果使用mutable,不仅函数的参数会发生改变,函数返回值也会发生变化,这个时候,为了不让客户端程序员更改,我们可以使用防御性复制的方法,即返回复制的副本。
这就是mutable和immutable的大概介绍,所有代码均参考MIT的课件,如果发现有任何问题,可以在评论区指出,方便我改正,谢谢。