mutable string vs. immutable string

1、若是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字符串的特色是:
① 单线程下对字符串的更改速度快。但多线程修改时为了保证正确性,须要加锁处理,影响性能。
② 占用内存空间小。
③ 若是按引用随意传递的话,将会难以追踪其变化。最好的方式是若是函数不修改它,就传递常引用。
 
2、若是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字符串的特色是:
① 不存在多线程并发修改问题,由于每次修改都是建立新对象,没有共享就没有“伤害”。
② 按引用传递也不用担忧其内容在其余地方被修改。
③ 占用空间较大,由于每次修改都要建立新对象(能够采用享元模式来解决)。
 
3、享元模式
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)在函数式编程和并发领域都会常常遇到,经过上面的对比,咱们应该就能清楚地体会到它的特色了。java