Skip to content

Latest commit

 

History

History
54 lines (35 loc) · 4.39 KB

File metadata and controls

54 lines (35 loc) · 4.39 KB

Back to questions

1ae9: Reusing immutable value objects

Important note: This question concerns optimising a piece of software for memory-efficiency. The purpose here, as usual, is to expose you to more of Java, and in particular to get you to think about heap memory usage. In general, it is not a good idea to speculatively optimise software, either for performance or memory-efficiency. Such optimisations should be guided by profiling: testing a piece of software on real data to work out where the efficiency bottlenecks are. Optimisations make code harder to understand and maintain. So, while we will explore an optimisation in this questions, the message is not that you should apply this kind of optimisation by default.

Consider the following Point class, which is very similar to the Point class you created and worked with in question 0f05:

Point.java

Observe that instances of Point are immutable. We sometimes call immutable objects value objects because they be used in a similar way to primitive values: because they cannot change, they do not suffer from problems of aliasing that are relevant for mutable objects.

Suppose an application is likely to create a vast number of Point objects, but that many of these objects are likely to be identical. For efficient memory usage, in this case it might be preferable to keep a pool of points that have already been created, and when asked for a new point to return a matching point in the pool, if one exists.

Your task is to implement this scenario. Make the constructor of Point private, so that clients cannot create points directly. In Point, declare a static method, makePoint, with the following signature:

public static Point makePoint(int x, int y, int z);

This static method will be the single place where points can be created; such a method is called a factory method.

The aim of makePoint is to cache all points that have been previously created. If makePoint is invoked with the coordinates of a point that already exists, a reference to the existing point will be returned. This can save memory by avoiding the client holding references to many identical points stored in distinct objects.

To achieve the desired effect of makePoint, equip the Point with a private static field that maps points to points:

private static Map<Point, Point> pool = new HashMap<Point, Point>();

This makes use of the Map interface from java.util, and one of its implementing classes, HashMap. Somewhat strangely, we are going to use pool to map a Point object reference to itself! You may ask, why can't we just use a Set? The reason is that we will wish to ask: "does the pool contain a Point that is equal to some new Point?", and if the answer is "yes", we wish to get the original Point contained in the pool. While a Set would allow us to ask the question, it does not provide an efficient way for us to get at the reference to the original Point. (We could iterate through the whole set until we find the reference of interest, but this would be highly inefficient.)

Your implementation of makePoint should do the following:

  • Construct a new Point with the given coordinates, and store the result in a reference variable (say p)

  • Test whether pool maps p to some point. This can be ascertained by asking whether pool.get(p) is null

  • If pool does map p to some point then makePoint should return the Point to which p is mapped. As a result, the new object to which p points becomes unreachable, and will be garbage-collected

  • If pool does not map p to some point then makePoint should add p to the pool by using the put(Point, Point) method of Map to map p to itself. After this, p should be returned

To convince yourself that your implementation is indeed saving memory, write two main methods. Each should create a list of points, and add 10 identical points to the list. The first program should use the original Point class, creating 10 distinct points. The second should use the new Point class with its makePoint factory method. In the first instance you should write a method that checks whether the references in your list are all different. In the second case you should write a method that checks whether the references in your list are all the same.