一、如果String是mutable的,如C++的std::string
看下面的例子:
void foo1(string s) {
s[0] = 'b';
}
void foo2(string &s) {
s[0] = 'b';
}
int main(void) {
string s1 = "hello";
string s2 = "hello";
foo1(s1);
foo2(s2);
// hello
cout << s1 << endl;
// bello,s2变量的值被修改了
cout << s2 << endl;
}
mutable字符串的特点是:
① 单线程下对字符串的更改速度快。但多线程修改时为了保证正确性,需要加锁处理,影响性能。
② 占用内存空间小。
③ 如果按引用随意传递的话,将会难以追踪其变化。最好的方式是如果函数不修改它,就传递常引用。
二、如果String是immutable,如jdk的java.lang.String
看下面的例子:
static void foo(String s) {
s = "java";
}
public static void main(String[] args) {
String s = "hello";
foo(s);
// hello
System.out.println(s);
}
immutable字符串的特点是:
① 不存在多线程并发修改问题,因为每次修改都是创建新对象,没有共享就没有“伤害”。
② 按引用传递也不用担心其内容在其他地方被修改。
③ 占用空间较大,因为每次修改都要创建新对象(可以采用享元模式来解决)。
三、享元模式
immutable String有个缺点就是每次修改都要创建新对象,这样占用内存空间较多,而且频繁创建对象时间开销也多。
一个很好的解决办法就是使用享元模式。既然对象是不可变的,那么它就可以被缓存下来,以后如果需要同样的对象,就直接从缓存池里复用该对象,而不是又创建一个新对象。
例如java jdk中的Integer类:
public class Integer {
public static Integer valueOf(int i) {
// 如果i的范围在[low, high],则从缓存池里获取
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
}
private static class IntegerCache {
// 最小值不能被改变
static final int low = -128;
// 最大值可以通过-Djava.lang.Integer.IntegerCache.high来设置
// 默认值是127
static final int high;
static final Integer cache[];
static {
...
// 初始化high的值
...
// 初始化缓存池
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
}
...
}
}
不可变对象(immutable object)在函数式编程和并发领域都会经常遇到,通过上面的对比,我们应该就能清楚地体会到它的特点了。
来源:oschina
链接:https://my.oschina.net/u/2504786/blog/3188822