mutable string vs. immutable string

mutable string vs. immutable 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)在函数式编程和并发领域都会经常遇到,通过上面的对比,我们应该就能清楚地体会到它的特点了。