Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Is it Really Better to 'Return an Empty List Instead of null'? - Part 3

0.00/5 (No votes)
8 Jan 2015 1  
Part III: Empty Lists in Real Life

Table of Contents

Home

Part III: Empty Lists in Real Life

Introduction

In the previous installment, we saw why it is better to return null instead of an empty list if a function is called with 'no data' to return. For example, the following Java function ...

Java
public static List<ICustomerOrder> getOrdersByCustomer ( String customerID )

... should return null (and not an empty list) if no orders exist for a given customer. As we saw, this reduces the risk of bad outcomes due to a bug and has other advantages.

In this part of the article series, we will look at how we use lists in real life.

When thinking about how to design or implement a piece of software, I find it often useful and illuminating to consider how things work in real life.

So, what can we say about empty lists in real life?

Do they exist?

Do they occur frequently?

Do real life examples confirm our conclusion from the previous installment?

Let's see.

Alice's Shopping List

Imagine the following story of Alice and Bob:

Alice: "Bob, could you please go to the groceries and buy some food?"

Bob: "Yes, of course!"

Alice gives Bob a shopping list.

Bob drives to the groceries. He looks at the list and sees that it is ... empty.

Alice's shopping list

Alice's shopping list

Bob drives back home and gives Alice an empty box.

Bob: "Here is the food you asked me to buy."

Alice: "Thank you darling."

What we perceive as ridiculous, stupid or funny in real life has happened (and will happen) billions of times in the world of software execution. It happens every time (in avoid-null environments) a function returns an empty list (instead of null) to denote 'no data'.

One might argue that handling an empty list in software doesn't consume considerable time and resources - in contrast to Bob who spent time and used his car. It is true that the time and resources needed to handle an empty list is negligible in many cases. Nevertheless, there is a time and space penalty, and the consequences can be dreadful. Just imagine a worst case scenario of a network connection that must be established and data that has to be exchanged between computers to handle an empty list. And then, once in a while, there might be a network connection failure which leads to a total system crash because the operation 'do nothing' could not be executed. This would be similar to Bob having a car engine breakdown (or something worse happening) when he drives to the groceries to 'buy nothing'.

On the other hand, as we saw in the previous installment, null is always cheap in terms of time and space.

Bob's Postage Stamps

Here is another real-life example:

Bob collects postage stamps.

Alice doesn't collect postage stamps.

Bob has a box labelled 'Postage stamps' containing his stamps.

Does this mean that Alice has an empty postage-stamps-box because she doesn't collect postage stamps?

No, of course not! She simply doesn't have a box for stamps.

Just imagine everybody in the world had an empty box for everything he/she doesn't collect. Weird!

So, what should getPostageStamps in the following Java interface return if a person doesn't collect stamps? An empty list or null?

Java
interface IPerson {

   public String getName();
   // ... more attributes

   public List<IPostageStamp> getPostageStamps();
}

Returning an empty list is like Alice having an empty box.

Returning null is like Alice having no box at all.

[Note] Note

In languages that support the Optional/Maybe pattern, an alternative solution would be to always return a non-null Optional or Maybe object. The basic idea of the Optional/Maybe pattern is this:

Instead of providing a non-null value or null, always provide a non-null container object that either contains a value or doesn't contain a value.

Please refer to my previous article Why We Should Love 'null' (chapter 'The Optional/Maybe Pattern') for a discussion of this solution.

Is this pattern popular in real life? I don't think so. Just imagine an application of this pattern in our example. Alice doesn't collect stamps. Therefore she has a box that contains ... nothing. Bob collects postage stamps. So, he has a box that contains a box that contains postage stamps.

Customers in Luxembourg

Suppose we have the following method to get a list of customers by city:

Java
public static List<ICustomer> getCustomersByCity ( String city ) {
   // code to retrieve customers from database and return the result
}

What should this method return if there are no customers in the database for a given city?

If we read through the many forums that discuss this kind of question, we can see that we typically get the following three different answers, sorted by popularity (the first one being the most popular one):

  1. The method should return an empty list
  2. The method should return null
  3. The method should throw an exception

Another important question is this: How should this method behave in case of a resource error such as a database connection error at runtime? Again, depending on who you ask, you'll get different answers.

To find the correct answers, let us think about how such a case would be handled in real life.

Imagine:

Big boss to assistant: "I need a list of all our customers in Luxembourg. Could you do that for me please?"

The assistant obliges. His or her task is to launch a query in the company's ERP software in order to print out a list of customers in Luxembourg.

There are three possible outcomes:

  1. The company has customers in Luxembourg:

    The assistant prints out the list and hands it over to the boss.
  2. The company doesn't have customers in Luxembourg:

    The assistant tells his or her boss: "We don't have customers in Luxembourg".

    Important: Under normal conditions, the assistant wouldn't give an empty, white piece of paper to the boss to signal the fact that there are no customers, would he?

  3. The assistant can't execute the query because of a technical problem (for example: he forgot his password (because he changed it in the morning (but forgot to write it down on a post-it that is kept at a secret place (i.e. stuck on the front of his desktop monitor)))):

    The assistant tells his boss "Sorry, I couldn't print the list because ...".

Now the correct answers to our software design questions become pretty much obvious, don't they?

  1. No need to discuss the first case. If there are customers in Luxembourg, then getCustomersByCity returns a non-empty list.

  2. If we model the real world, then we obviously return null in case 2 (no customers in Luxembourg). Returning an empty list would be like the assistant handing over an empty piece of paper to his boss. Nobody would do this in real life - it wouldn't make sense, at least not under 'normal' conditions.

    And there is no need to do it in software, unless we have a very good reason to do so.

    For example, imagine that the boss really needs a piece of paper, even if there are no customers. He/she wants a document like this one ...

    ... as a 'proof' to be filed somewhere.

    If we want to model this, we might be tempted to actually return an empty list. But a better solution would be to return a non-null object implementing the following Java interface:

    Java
    interface ICustomerByCityReport {
    
       public String getCity();
    
       public Date getDateOfReport();
    
       public List<ICustomer> getCustomers();
    }

    In case of no customers for a given city, the method would return a non-null ICustomerByCityReport object with getCustomers() returning null.

  3. Case 3 (i.e. the operation couldn't be executed because of a technical problem) is less obvious. Should we simply return null? No! Because that would be like the assistant telling his boss: "There are no customers in Luxembourg.". Obviously, the information "There are no customers in Luxembourg." is semantically very different from "I couldn't print the list because of a technical problem. I can't tell you if we have customers in Luxembourg or not". This is an important distinction for the boss, and only he/she can decide what to do. The same is true for client code that calls getCustomersByCity. The information "there was a technical problem" must be forwarded to the client and it is the client's role to decide what to do.

    How to do this depends on the programming language we use.

    In languages that support an exception mechanism (C#, Java, etc.), we would throw an exception if the data cannot be retrieved from the database (e.g. connection to database could not be established).

    In other languages (e.g. languages that support multiple output arguments or tuples) we might return two values - the first one being the result and the second one being the error. The following return states are then possible:

    • The operation succeeded and results were found: result holds a (non-empty) list and error is null

    • The operation succeeded but no results were found: result and error are null

    • The operation could not be executed: result is null and error contains an object describing the error

Empty Lists in Real Life

If we look around us in the physical world, we can quickly see that empty lists are very rare in real life. Most often, we either have a non-empty list or we have no list at all. For example, you might have a to-do list. If you have one, it is probably (like mine) not empty.

However, it is obvious, but also interesting to note that empty lists occur each time we start to create a non-empty list.

Imagine Alice jotting down a shopping list for Bob:

  1. She takes a piece of paper which is empty - an empty list.
  2. She writes down all the items to buy. At the end, the list is non-empty. Moreover, during the writing process, the list is mutable - items are added (and some might be removed or modified).
  3. Once the list has been created, it becomes immutable - the list doesn't change anymore.

This is how we typically proceed in real life. And this reflects exactly how we should proceed in software. Here is a trivial example in Java:

Java
public static List<String> getShoppingList() {

   // 1. create a mutable empty list
   List<String> result = new ArrayList<String>();

   // 2. populate the list      
   result.add ( "Almonds" ); 
   result.add ( "Coconut oil" );
   result.add ( "Avocado" );
   result.add ( "Blueberries" );

   // 3. return an immutable, non-empty list
   return Collections.unmodifiableList ( result );
}

Conclusion

Immutable empty lists are very rare in real life. Normally, we either have a non-empty list or there is no list at all.

The corollary is that we shouldn't use immutable empty lists in our software applications, unless there is an exceptional case. This matches our conclusion from the previous installment and confirms that it is generally better to return null instead of an empty list.

In the next installment, we will look at real-world source code examples. We will have a look at typical cases that happen frequently in practice. And we will see how to treat exceptional situations, such as invalid input argument values and resource errors.

Links to Related Articles

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here