What are some of the best practices relating to Java collection?

  • Use ArrayList, HashMap etc as opposed to Vector, Hashtable etc, where possible to avoid any synchronization overhead. Even better is to use just arrays where possible. If multiple threads concurrently access a collection and at least one of the threads either adds or deletes an entry into the collection, then the collection must be externally synchronized. This is achieved by:
    Map myMap = Collections.synchronizedMap (myMap); //conditional thread-safety
    List myList = Collections.synchronizedList (myList); //conditional thread-safety
  • Set the initial capacity of a collection appropriately (e.g. ArrayList, HashMap etc). This is because Collection classes like ArrayList, HashMap etc must grow periodically to accommodate new elements. But if you have a very large array, and you know the size in advance then you can speed things up by setting the initial size appropriately.
    For example: HashMaps/Hashtables need to be created with sufficiently large capacity to minimize rehashing (which happens every time the table grows). HashMap has two parameters initial capacity and load factor that affect its performance and space requirements. Higher load factor values (default load factor of 0.75 provides a good trade off between performance and space) will reduce the space cost but will increase the lookup cost of myMap.get(…) and myMap.put(…) methods. When the number of entries in the HashMap exceeds the current capacity * loadfactor then the capacity of the HasMap is roughly doubled by calling the rehash function. It is also very important not to set the initial capacity too high or load factor too low if iteration performance or reduction in space is important.
  • Program in terms of interface not implementation: For example you might decide a LinkedList is the best choice for some application, but then later decide ArrayList might be a better choice for performance reason.
    Use:
        List list = new ArrayList(100); // program in terms of interface & set the initial capacity.
    Instead of:
        ArrayList list = new ArrayList();
  • Return zero length collections or arrays as opposed to returning null: CO Returning null instead of zero length collection (use Collections.EMPTY_SET, Collections.EMPTY_LIST, Collections.EMPTY_MAP) is more error prone, since the programmer writing the calling method might forget to handle a return value of null.
  • Immutable objects should be used as keys for the HashMap: CO Generally you use a java.lang.Integer or a java.lang.String class as the key, which are immutable Java objects. If you define your own key class then it is a best practice to make the key class an immutable object (i.e. do not provide any setXXX() methods etc). If a programmer wants to insert a new key then he/she will always have to instantiate a new object (i.e. cannot mutate the existing key because immutable key object class has no setter methods).
  • Encapsulate collections: In general collections are not immutable objects. So care should be taken not to unintentionally expose the collection fields to the caller.
    Avoid where possible Better approach
    The following code snippet exposes the Set “setCars” directly to the caller. This approach is riskier because the variable “cars” can be modified unintentionally.
    public class CarYard{
    //…
    private Set cars = new HashSet();
    //exposes the cars to the caller
    public Set getCars() {
    return cars;
    }
    //exposes the cars to the caller
    public void setCars(Set cars) {
    this.cars = cars;
    }
    //…
    }
    This approach prevents the caller from directly using the underlying variable “cars”.
    public class CarYard{
    private Set cars = new HashSet();
    //…
    public void addCar(Car car) {
    cars.add(car);
    }
    public void removeCar(Car car) {
    cars.remove(car);
    }
    public Set getCars() {
    //use factory method from the Collections
    return Collections.unmodifiableSet (cars);
    }
    }
  • Avoid storing unrelated or different types of objects into same collection: This is analogous to storing items in pigeonholes without any labeling. To store items use value objects or data objects (as opposed to storing every attribute in an ArrayList or HashMap). Provide wrapper classes around your collections API classes like ArrayList, HashMap etc as shown in better approach column. Also where applicable consider using composite design pattern, where an object may represent a single object or a collection of objects. Refer Q61 in Java section for UML diagram of a composite design pattern. If you are using J2SE 5.0 then make use of “generics”.
    Avoid where possible Better approach
    The code below is hard to maintain and understand by others. Also gets more complicated as the requirements grow in the future because we are throwing different types of objects like Integer, String etc into a list just based on the indices and it is easy to make mistakes while casting the objects back during retrieval.
    List myOrder = new ArrayList()
    ResultSet rs = …
    While (rs.hasNext()) {
    List lineItem = new ArrayList();
    lineItem.add (new Integer(rs.getInt(“itemId”)));
    lineItem.add (rs.getString(“description”));
    ….
    myOrder.add( lineItem);
    }
    return myOrder;

    Example 2:
    List myOrder = new ArrayList(10);
    //create an order
    OrderVO header = new OrderVO();
    header.setOrderId(1001);

    //add all the line items
    LineItemVO line1 = new LineItemVO();
    line1.setLineItemId(1);
    LineItemVO line2 = new LineItemVO();
    Line2.setLineItemId(2);
    List lineItems = new ArrayList();
    lineItems.add(line1);
    lineItems.add(line2);
    //to store objects
    myOrder.add(order);// index 0 is an OrderVO object
    myOrder.add(lineItems);//index 1 is a List of line items
    //to retrieve objects
    myOrder.get(0);
    myOrder.get(1);

    Above approaches are bad because disparate objects are stored in the lineItem collection in example-1 and example-2 relies on indices to store disparate objects. The indices based approach and storing disparate objects are hard to maintain and understand because indices are hard coded and get scattered across the code. If an index position changes for some reason, then you will have to change every occurrence, otherwise it breaks your application.
    The above coding approaches are analogous to storing disparate items in a storage system without proper labeling and just relying on its grid position.
    When storing items into a collection define value objects as shown below: (VO is an acronym for Value Object).
    public class LineItemVO {
    private int itemId;
    private String productName;
    public int getLineItemId(){return accountId ;}
    public int getAccountName(){return accountName;}
    public void setLineItemId(int accountId ){
    this.accountId = accountId
    }
    //implement other getter & setter methods
    }

    Now let’s define our base wrapper class, which represents an order:
    public abstract class Order {
    int orderId;
    List lineItems = null;
    public abstract int countLineItems();
    public abstract boolean add(LineItemVO itemToAdd);
    public abstract boolean remove(LineItemVO itemToAdd);
    public abstract Iterator getIterator();
    public int getOrderId(){return this.orderId; }
    }

    Now a specific implementation of our wrapper class:
    public class OverseasOrder extends Order {
    public OverseasOrder(int inOrderId) {
    this.lineItems = new ArrayList(10);
    this.orderId = inOrderId;
    }
    public int countLineItems() { //logic to count }
    public boolean add(LineItemVO itemToAdd){
    …//additional logic or checks
    return lineItems.add(itemToAdd);
    }
    public boolean remove(LineItemVO itemToAdd){
    return lineItems.remove(itemToAdd);
    }
    public ListIterator getIterator(){ return lineItems.Iterator();}
    }

    Now to use:
    Order myOrder = new OverseasOrder(1234) ;
    LineItemVO item1 = new LineItemVO();
    Item1.setItemId(1);
    Item1.setProductName(“BBQ”);
    LineItemVO item2 = new LineItemVO();
    Item1.setItemId(2);
    Item1.setProductName(“Outdoor chair”);
    //to add line items to order
    myOrder.add(item1);
    myOrder.add(item2);
Tagged . Bookmark the permalink.

Leave a Reply