What is meant by immutable?

Solution 1:

Immutable means that once the constructor for an object has completed execution that instance can't be altered.

This is useful as it means you can pass references to the object around, without worrying that someone else is going to change its contents. Especially when dealing with concurrency, there are no locking issues with objects that never change

e.g.

class Foo
{
     private final String myvar;

     public Foo(final String initialValue)
     {
         this.myvar = initialValue;
     }

     public String getValue()
     {
         return this.myvar;
     }
}

Foo doesn't have to worry that the caller to getValue() might change the text in the string.

If you imagine a similar class to Foo, but with a StringBuilder rather than a String as a member, you can see that a caller to getValue() would be able to alter the StringBuilder attribute of a Foo instance.

Also beware of the different kinds of immutability you might find: Eric Lippert wrote a blog article about this. Basically you can have objects whose interface is immutable but behind the scenes actual mutables private state (and therefore can't be shared safely between threads).

Solution 2:

An immutable object is an object where the internal fields (or at least, all the internal fields that affect its external behavior) cannot be changed.

There are a lot of advantages to immutable strings:

Performance: Take the following operation:

String substring = fullstring.substring(x,y);

The underlying C for the substring() method is probably something like this:

// Assume string is stored like this:
struct String { char* characters; unsigned int length; };

// Passing pointers because Java is pass-by-reference
struct String* substring(struct String* in, unsigned int begin, unsigned int end)
{
    struct String* out = malloc(sizeof(struct String));
    out->characters = in->characters + begin;
    out->length = end - begin;
    return out;
}

Note that none of the characters have to be copied! If the String object were mutable (the characters could change later) then you would have to copy all the characters, otherwise changes to characters in the substring would be reflected in the other string later.

Concurrency: If the internal structure of an immutable object is valid, it will always be valid. There's no chance that different threads can create an invalid state within that object. Hence, immutable objects are Thread Safe.

Garbage collection: It's much easier for the garbage collector to make logical decisions about immutable objects.

However, there are also downsides to immutability:

Performance: Wait, I thought you said performance was an upside of immutability! Well, it is sometimes, but not always. Take the following code:

foo = foo.substring(0,4) + "a" + foo.substring(5);  // foo is a String
bar.replace(4,5,"a"); // bar is a StringBuilder

The two lines both replace the fourth character with the letter "a". Not only is the second piece of code more readable, it's faster. Look at how you would have to do the underlying code for foo. The substrings are easy, but now because there's already a character at space five and something else might be referencing foo, you can't just change it; you have to copy the whole string (of course some of this functionality is abstracted into functions in the real underlying C, but the point here is to show the code that gets executed all in one place).

struct String* concatenate(struct String* first, struct String* second)
{
    struct String* new = malloc(sizeof(struct String));
    new->length = first->length + second->length;

    new->characters = malloc(new->length);

    int i;

    for(i = 0; i < first->length; i++)
        new->characters[i] = first->characters[i];

    for(; i - first->length < second->length; i++)
        new->characters[i] = second->characters[i - first->length];

    return new;
}

// The code that executes
struct String* astring;
char a = 'a';
astring->characters = &a;
astring->length = 1;
foo = concatenate(concatenate(slice(foo,0,4),astring),slice(foo,5,foo->length));

Note that concatenate gets called twice meaning that the entire string has to be looped through! Compare this to the C code for the bar operation:

bar->characters[4] = 'a';

The mutable string operation is obviously much faster.

In Conclusion: In most cases, you want an immutable string. But if you need to do a lot of appending and inserting into a string, you need the mutability for speed. If you want the concurrency safety and garbage collection benefits with it the key is to keep your mutable objects local to a method:

// This will have awful performance if you don't use mutable strings
String join(String[] strings, String separator)
{
    StringBuilder mutable;
    boolean first = true;

    for(int i = 0; i < strings.length; i++)
    {
        if(!first) first = false;
        else mutable.append(separator);

        mutable.append(strings[i]);
    }

    return mutable.toString();
}

Since the mutable object is a local reference, you don't have to worry about concurrency safety (only one thread ever touches it). And since it isn't referenced anywhere else, it is only allocated on the stack, so it is deallocated as soon as the function call is finished (you don't have to worry about garbage collection). And you get all the performance benefits of both mutability and immutability.

Solution 3:

Actually String is not immutable if you use the wikipedia definition suggested above.

String's state does change post construction. Take a look at the hashcode() method. String caches the hashcode value in a local field but does not calculate it until the first call of hashcode(). This lazy evaluation of hashcode places String in an interesting position as an immutable object whose state changes, but it cannot be observed to have changed without using reflection.

So maybe the definition of immutable should be an object that cannot be observed to have changed.

If the state changes in an immutable object after it has been created but no-one can see it (without reflection) is the object still immutable?

Solution 4:

Immutable objects are objects that can't be changed programmatically. They're especially good for multi-threaded environments or other environments where more than one process is able to alter (mutate) the values in an object.

Just to clarify, however, StringBuilder is actually a mutable object, not an immutable one. A regular java String is immutable (meaning that once it's been created you cannot change the underlying string without changing the object).

For example, let's say that I have a class called ColoredString that has a String value and a String color:

public class ColoredString {

    private String color;
    private String string;

    public ColoredString(String color, String string) {
        this.color  = color;
        this.string = string;
    }

    public String getColor()  { return this.color;  }
    public String getString() { return this.string; }

    public void setColor(String newColor) {
        this.color = newColor;
    }

}

In this example, the ColoredString is said to be mutable because you can change (mutate) one of its key properties without creating a new ColoredString class. The reason why this may be bad is, for example, let's say you have a GUI application which has multiple threads and you are using ColoredStrings to print data to the window. If you have an instance of ColoredString which was created as

new ColoredString("Blue", "This is a blue string!");

Then you would expect the string to always be "Blue". If another thread, however, got ahold of this instance and called

blueString.setColor("Red");

You would suddenly, and probably unexpectedly, now have a "Red" string when you wanted a "Blue" one. Because of this, immutable objects are almost always preferred when passing instances of objects around. When you have a case where mutable objects are really necessary, then you would typically guard the objet by only passing copies out from your specific field of control.

To recap, in Java, java.lang.String is an immutable object (it cannot be changed once it's created) and java.lang.StringBuilder is a mutable object because it can be changed without creating a new instance.