Show TOC Start of Content Area

Background documentation Program Flow  Locate the document in its SAP Library structure

The Check-and-Get Idiom

A very common idiom is the check-and-get code pattern. The code pattern has the following steps:

·        Check for availability of data that match a query criteria.

·        Retrieve the data that match the query criteria.

While the approach to check the availability of data before getting the data is straightforward, it has a bad impact on application performance.

Example: Check-and-get in maps

if(map.contains(myKey)) {  // check
  
myValue = map.get(myKey);  // get
  
..

The problem with this idiom is that the get operation always includes the activity that is performed by the checkoperation. The difference is that the latter does a lookup (Example for lookup: finding a row in a table, finding a key in a hash-table, locating an object in a list) but throws away the results. As (in the idiom) the get operation is always called when check was successful, we have a duplication of efforts in the positive case. The negative case (check fails) does not compensate this drawback, since a getoperation returning no results is (in most cases) just as complex as a check operation returning false.

Recommendation

Avoid Extra Checks

Checks like hasChildren, isEmpty, contains and so on should be avoided, whenever the data/objects whose availability is checked are retrieved later anyway. This is in particular the case when the availability check involves complex processing like in database-access or access to connected systems.

 

Examples for redundant checks (check-and-get idiom)

Applies to

Check Operation

Get Operation

java.util.Map

Contains

get

java.util.Collection

isEmpty

Iterator

com.sapportals.wcm.repository
ICollection

hasChildren

getChildren

SQL

select COUNT(ID) from ... where

Select ID, COL1, COL2, COLn from … where

 

Null-Checking and NullPointerException

The creation and throwing of exceptions is an expensive operation in Java. Therefore exceptions should not be provoked without good reason.

Some programmers avoid explicit null-checks by catching the corresponding runtime exception NullPointerException, thinking the source code is executed faster because an if-statement has been saved. In fact the cost of the exception is so huge that a single null value in 1.000.000 cases will be enough to eradicate the performance advantage of the saved if statements. So this technique does not save time but will lead to noticeable performance degradation when null values occur more often.

Conclusion: NullPointerExceptions should serve their purpose to indicate programming errors and nothing else.

Process documentation

The JLin rule NullPointer Check will find the catch blocks for NullPointerExceptions.

Loops

Counting Loops vs. Iterators

The common denominator of all Collection Classes is the interface Collection, which is implemented for all lists and sets and for the keys and values of maps. All collections support sequential iteration using the Iterator interface.

The traditional C-style iteration idiom using an integer index has no advantage in terms of performance or memory requirements unless the index is also used for another purpose. The following example shows a popular idiom.

for (int i=0; i < list.size(); i++) {
   doSomething(list.get(i));
}

 

The example has following flaws:

·         The constant expression size() is repeatedly evaluated.

·         The class of the list object is required to implement the List  interface (strong precondition)

·         This iteration pattern is very slow for LinkedLists and other implementations that do not offer fast random access (indicated by the RandomAccess interface).

Note

Use index-based access only when needed. Index-based access often precludes the use of efficient data structures based on hash-codes or balanced trees.

 

Following implementation alternatives for the example:

for(final Iterator iter = list.iterator(); iter.hasNext();) {

   doSomething(iter.next());

}

or, if index-based access is needed:

for(int i = 0, n = list.size(); i < n; i++) {

   doSomething(list.get(i));

}

 

Iterating through maps

A often ignored method in the Map interface is the entrySet method. Many applications emulate the entrySet method by using the keySet method and get for every entry in the set:

Iterator iter = map.keySet().iterator();

while (iter.hasNext()) {

   Object key = iter.next();

   Object value = map.get(key);

  

}

This work around does the job but the performance is less not acceptable. Consider that the lookup operation get is called for every element in the map.

We recommend the following implementation that works without a lookup:

Iterator iter = map.entrySet().iterator();

while (iter.hasNext()) {

   Map.Entry e = (Map.Entry) iter.next();

   Object value = e.getValue();

  

}

 

Process documentation

Use the JLin Rule Map Iteration  to find suspicious places in your source code.

Repeated Evaluation of Invariant Expressions

Everything inside a loop is processed with every loop. Therefore the loop must contain only statements that have to be processed in the loop. Each expression that does not change during the loop execution should be evaluated and assigned to a local variable before the loop is entered.

for (Iterator iter = list.iterator(); iter.hasNext();) {

   MyObject obj = (MyObject) iter.next();

   PropertyName pn = new PropertyName(

             bean.getPortalCustomNamespace(),

             Settings.RES_PROPERTY);

   if (obj.getProperty(pn).toString()

            .equalsIgnoreCase(Settings.RES_THUMB) {

 

   }

}

 

The statement that violates the rule is highlighted in this example. The only variable that changes on every cycle is the obj variable. Since this variable is not a parameter of the creation of the PropertyName object we can be sure that the value of variable pn is the same for every iteration cycle.

We can also rule out that pn is changed in the cycle, so we can be sure that the meaning of the loop is not changed when we use the same PropertyName instance on every iteration instead of several equal ones. 

PropertyName pn = new PropertyName(

             bean.getPortalCustomNamespace(),

             Settings.RES_PROPERTY);

for (Iterator iter = list.iterator(); iter.hasNext();) {

   MyObject obj = (MyObject) iter.next(); 

   if (obj.getProperty(pn).toString()

              .equalsIgnoreCase(Settings.RES_THUMB) {

 

   }

}

 

End of Content Area