Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

Mutable or immutable?

Techniques for defensive programming

  • Print
  • Feedback

Page 2 of 2

Mutable class with a convert-to-immutable-version method

The previous pattern is suitable for read-only data whose usage is short-lived (e.g., scoped to a method). It trades efficiency for a weaker form of immutability: changes in the underlying data remain visible to the client code. For stronger immutability, I can do the following: create an immutable class with a toImmutable() method that locks in the data by returning a copy of it. Consider these two classes implementing a simplified version of a sorted int vector:

package vector;
public class SortedVector
{
    public SortedVector (int capacity)
    {
        m_data = new int [capacity];
    }
    
    // ACCESSORS: [all methods are final]
    
    public final int size ()
    {
        return m_size;
    }
    public final int get (int index)
    {
        return m_data [index];
    }
    
    // package-private constructor:    
    SortedVector (MutableSortedVector item)
    {
        // Note: 'm_data' array is not cloned here and is rather shared
        // by reference with the original MutableSortedVector until
        // the next MutableSortedVector mutator method is invoked [if ever]
        
        m_data = item.m_data;
        m_size = item.m_size;
    }
    
    int [] m_data;
    int m_size;
}
package vector;
public final class MutableSortedVector extends SortedVector
{
    public MutableSortedVector (int capacity)
    {
        super (capacity);
    }
    public SortedVector toImmutable ()
    {
        m_shared = true; // set 'shared' flag
        return new SortedVector (this);
    }
    
    // MUTATORS:
    // Add a new value; keep them in increasing order:
    public void add (int value)
    {
        // [This simple implementation is O(size) and is used for
        // illustrative purposes only]
        
        if (m_size == m_data.length)
            throw new IllegalStateException ("maximum capacity reached");
        // If the data array is shared, it must be cloned before we can
        // proceed [copy-on-write]:
        if (m_shared)
        {
            m_data = (int []) m_data.clone ();
            m_shared = false; // reset 'shared' flag
        }               
        
        // Insert the new value after a simple linear scan:        
        int position;    
        for (position = 0; position < m_size; ++ position)
        {
            if (m_data [position] > value) break;
        }
        
        if (position < m_size)
            System.arraycopy (m_data, position,
                              m_data, position + 1, m_size - position);
            
        m_data [position] = value;
        ++ m_size;
    }
    
    private boolean m_shared; // if 'true', next mutator call must clone data
}


Even though both classes are public, observe how toImmutable() relies on a special package-private constructor provided by SortedVector for MutableSortedVector and nobody else. Implementing such conversion methods usually implies extra data copying and extra performance hits. But if I know that the mutable instance will always be discarded right after it has been converted, I can simply pass pieces of the internal MutableSortedVector state to the SortedVector constructor by reference to reduce overhead. Being certain of this knowledge is generally difficult, however, so I handle the issue using a traditional compromise technique called copy-on-write. Observe carefully how the mutator and toImmutable() methods use the m_shared flag to copy data only when necessary.

The implementation technique outlined above is much like the one used by String and StringBuffer companion classes in the standard Java implementation, except that the mutable StringBuffer is not an extension of the immutable String.

About the author

Vladimir Roubtsov has programmed in a variety of languages for more than 12 years, including Java since 1995. Currently, he develops enterprise software as a senior developer for Trilogy in Austin, Texas.
  • Print
  • Feedback

Resources