Showing posts with label java-practises. Show all posts
Showing posts with label java-practises. Show all posts

Wednesday, 22 June 2011

Beware of String functions - they may use simple strings as regex

Some string function may look simple and perform task accordingly but surprise you sometimes. eg. consider the Split function:

public class LineParser {
private final String[] values;

public LineParser(String line, String separator) {
values = line.split(separator);
}

public String getValue(int index) {
return values[index];
}
}

It’s a simple class that encapsulates parsing a text line and stores the result. Let's see.
public static void main(String[] args) {
LineParser parser1 = new LineParser("A,B,C", ",");
System.out.println("parser1:" + parser1.getValue(1));

LineParser parser2 = new LineParser("A B C", " ");
System.out.println("parser2:" + parser2.getValue(1));

LineParser parser3 = new LineParser("A|B|C", "|");
System.out.println("parser3:" + parser3.getValue(1));

LineParser parser4 = new LineParser("A\\B\\C", "\\");
System.out.println("parser4:" + parser4.getValue(1));
}

Output
For the first and second parser there is no surprise: the second value is ‘B’ and that’s exactly what gets printed. The third one instead of a second value prints ‘A’ – the first one… If that’s not strange enough the last parser throws an exception! That’s really unexpected!!

So where’s the catch? What’s wrong? Some of you already knew it, some probably start to suspect it… It’s all because of String.split() method – instead of taking a separator String as a parameter (which I tried to silently imply in the code) it takes a regular expression. Because of that two last parsers failed – both pipe and backslash signs have special meaning in Java regexps!
Mystery solved, so problem is gone… is it really? Of course you might be tempted just to fix the snippet above by writing the regexps correctly – this would be fine for this code. Now go home and check your code: do you use user-provided values in String.split()? What about String.replaceAll()? If you do you might be in real trouble… The real lesson is that some of the String methods take as a parameter plain Strings (eg: String.regionMatches()) while other expect a String with a regular expression (eg: String.matches()). Beware and double check!


Avoid subtraction based comparison between integers

Before I say anything I want to share with you a code snippet that is a simplified version of something I have recently wrote in my work. There is a small, yet painful bug in this code… can you see it?

public static class Point implements Comparable<Point> {
private int x, y;

public Point(int x, int y) {
this.x = x;
this.y = y;
}

@Override
public int compareTo(Point other) {
if (this.x != other.x) {
return this.x - other.x;
} else {
return this.y - other.y;
}
}

@Override
public boolean equals(Object obj) {
if (!(obj instanceof Point)) return false;
Point other = (Point) obj;
return (this.x == other.x && this.y == other.y);
}

@Override
public int hashCode() {
return 31 * x + y;
}
}

This is a simple value class represanting a point. At first glance everything is good with it: the hash function is not sophisticated, but it is correct and legal. The equals() method is written ‘by the book’ so it is probably OK. The class implements Comparable interface and defines a lexicographic order of the points – the code of compareTo() is standard and fulfills the contract of the interface. All three methods are compatible with each other: hash codes of equal points are equal, comparisons are transitive, if A.compareTo(B) == 0 then A.equals(B)… Basically all is fine, so where is the bug? Is there any bug at all?
Unfortunatelly there is a bug in this code. What is worse you probably read about it many times (’Effective Java’ item 11, ‘Java Puzzlers’ puzzle 65, the list goes on an on…). The problem with this code is that substraction based comparators are BAD as they often exposes you to danger of an overflow. This is what happens in this case – see the following:

public static void main(String[] args) {
Point pZero = new Point(0, 0);
Point pPlus = new Point(2000000000, 0);
Point pMinus = new Point(-2000000000, 0);

System.out.println( pPlus.compareTo(pZero) > 0 );
System.out.println( pZero.compareTo(pMinus) > 0 );
System.out.println( pPlus.compareTo(pMinus) > 0 );
}

We are comparing in this code three points. When you look at the declaration of those object you can already see the order of them: pPlus > pZero > pMinus. Just to be sure we check it by printing to console the result of compareTo() function. If you execute this code you’ll get that as expected ‘true’ for pPlus > pZero and ‘true’ for pZero > pMinus. The suprizing thing is the last comparison that evaluates to false. This is because of the integer overflow: in pPlus.compareTo(pMinus) the result value is 2000000000 – (-2000000000) == -294967296! Scary, right?

Saturday, 18 June 2011

Java Collection Best Practices

1. Decide approximate initial capacity of a collection -  As Collection implementations like ArrayList, HashMap, use Array internally for storing, you can speed up things  by setting initial capacity.

2. Use ArrayLIst, HahMap etc instead of Vector , HashTable to avoid synchronization overhead. Even better use array where ever possible.

3. Program to interface instead of implementation – It gives freedom of switching implementation without touching code.

For Example : Use

List list = new ArrayList();

Avoid :

ArrayList list = new ArrayList();

4. Always return empty collection object instead of null .

For Example : Use

public List getCustomer(){
        //...
        return new ArrayList(0);
}

Avoid :

public List getCustomer(){
        //...
        return null;
    }

5. Generally you use a java.lang.Integer or
a java.lang.String class as the key, which are immutable Java objects.

6. Avoid exposing collection field – Always provide fine grained methods for collection fields instead of directly exposing setter/getter for collection field.

For Example Use :

class  Order
{
    List ArrayLisy itemList;
    //...

    public boolean addItem(Item item){
        //....
    }

    public boolean removeItem(Item item){
        //....
    }
}

Avoid :

class  Order
{
    List ArrayLisy itemList;
    //...

    public void setItems(List items){
        //....
    }

  }

7. Avoid storing unrelated or different types of objects into same collection

For Example Use :

public class  Order
{
    List ArrayLisy itemList;
    //...

    public boolean addItem(Item item){
        //....
    }

    public boolean removeItem(Item item){
        //....
    }

    public static void main(String [] args){
        int id;
        String desc;
        Order order = new Order();
        //...

        Item item = new Item()[
        item.setItem(new Integer(id));
        item.setDesc(dec);
        order.addItem(item);
        //...
    }

}

public class Item
{
    int id;
    String desc;

    public void setId(int id){
        //..
    }

    public int getId(){
        //..
    }

    public void setDesc(){
        //..
    }

    public String getDesc(){
        //..
    }
}

Avoid :

public class  Order
{
    List ArrayLisy itemList;
    //...

    public boolean addItem(Item item){
        //....
    }

    public boolean removeItem(Item item){
        //....
    }

    public static void main(String [] args){
        int id;
        String desc;
        Order order = new Order();
        //...

        List item = new ArrayList()[
        item.add(new Integer(id));
        item.add(dec);
        order.addItem(item);
        //...
    }

   }

8. Use Collection framework features instead of traditional approach.

For Example: Use

//search for duplicate names
for (int i=0;i<nameArray.length ;i++ )
{
    String name = nameArray[i];
    if(nameSet.contains(name)){
        System.out.println("Got Duplicate name = " + name );
    }
}

Avoid:

//search for duplicate names

for (int i=0;i<nameArray.length ;i++ )
{   
    String name = nameArray[i];
    for(int j=0;j<nameArrayLength;j++){
        if(nameArray[j].equals(name)){
            System.out.println("Got Duplicate name = " + name );
        }
    }
}

Tuesday, 17 May 2011

Beware of floating numbers in java

Outside of a scientific or engineering context, the use of float and double (and the corresponding wrapper classes Float and Double ) should likely be avoided. The fundamental problem is that rounding errors will always occur when using these data types - they are unavoidable.

In a typical business application, using float and double to represent money values is dangerous, because of these rounding issues. Instead, BigDecimal should usually be used to represent money.

Avoid excessive use of the instanceof operator

Many hold that the instanceof operator should be used only as a last resort, and that an overridden method is usually (but not always) a better alternative.

This is because, if one object is of type T1, it wouldn't guarantee that object T2, which returns True in case of (T2 instanceof T1), may be of type T1. This is because T2 may belong to subclass of T1, as instanceof returns true than as well. See how we used it in equals method.

Thursday, 24 March 2011

Initialling to zero rather than null

Some classes like BigDecimal,String have empty element into it. So we should use them nicely. Initialize these to zero, rather than null.

public void someFunc(BigDecimal p_number)
{
BigDecimal m_number;
m_number = p_number==null?BigDecimal.Zero : p_number;
}

Tuesday, 15 February 2011

Avoid @throws in javadoc

Some argue that @throws should not be used at all. Instead, one may simply rely on the javadoc tool to automatically document all exceptions placed in the throws clause.

Checked Exceptions :

  • declaring only checked exceptions in the method's throws clause is a widely followed convention
  • javadoc automatically generates basic documentation for all exceptions in the throws clause
  • the documentation is generated with no extra effort on your part
Unchecked Exceptions :
  • aren't typically placed in the method's throws clause.
  • are very rarely caught by the caller.
  • do not form part of the contract of the method, but rather represent what happens when the contract is broken. The caller cannot recover from such errors.
  • almost always occur when a precondition on a parameter is not met. However, such conditions are almost always already documented in a @param tag.
Therefore, if you :
  • only place checked exceptions in the throws clause
  • don't use @throws at all
then only checked exceptions will appear in javadoc. It can be argued that this is beneficial: since checked exceptions are more important than unchecked ones, it's best that they stand out in javadoc, without being mixed in with other exceptions of minor interest.

In almost all cases, a @throws tag simply repeats verbatim conditions already stated in a @param tag, and doesn't add in any way to the specification of the method's behavior. Such repetition should be regarded with grave suspicion. When a change occurs, it's far too easy to forget to update the javadoc in two separate places.

A general comment regarding broken contracts can be stated once in the javadoc overview.html document :
"If the requirements or promises of any method's contract are not fulfilled (that is, if there is a bug in either the method or its caller), then an unchecked exception will be thrown. The precise type of such an unchecked exception does not form part of any method's contract."

Example

BasketBall has two constructors.

The first constructor includes several @throws tags in its javadoc. However, aside from the type of the unchecked exception, all of these @throws tags are logically equivalent to some previous statement in a @param tag. They add nothing to the contract.

The second constructor follows a different style. It has a single parameter, and the conditions on this parameter are stated once (and once only) in its @param tag.

public final class BasketBall {
  /**
  * @param aManufacturer non-null and has visible content.
  * @param aDiameter in centimeters, in the range 1..50.
  * @throws IllegalArgumentException if aDiameter not in given range.
  * @throws IllegalArgumentException if aManufacturer has no visible content.
  * @throws NullPointerException if aManufacturer is null.
  */
  BasketBall( String aManufacturer, int aDiameter ){
    //..elided
  }
  /**
  * @param aDiameter in centimeters, in the range 1..50.
  */
  BasketBall( int aDiameter ){
    //..elided
  }
  // PRIVATE //
  private String fManufacturer;
  private int fDiameter;
}