Click here to Skip to main content
15,889,176 members
Articles / Programming Languages / Java

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

Rate me:
Please Sign up or sign in to vote.
4.25/5 (36 votes)
8 Jan 2015LGPL39 min read 75.9K   293   16   79
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, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)


Written By
Software Developer
Luxembourg Luxembourg
Curious programmer. Zealous simplifier. Lifelong student of life.
I care about creating software that is easy to use, reliable, and maintainable.
https://www.cnpp.dev

Comments and Discussions

 
Question"Null" does not represent an non existent list. Pin
Danilo Mendonça Oliveira25-Jul-18 2:54
Danilo Mendonça Oliveira25-Jul-18 2:54 
GeneralNull lists are not empty lists. Pin
brianmfear9-Feb-17 5:02
brianmfear9-Feb-17 5:02 
GeneralMy vote of 2 Pin
Erik Funkenbusch26-Jan-15 6:06
Erik Funkenbusch26-Jan-15 6:06 
GeneralRe: My vote of 2 Pin
ChristianNeumanns27-Jan-15 21:26
mvaChristianNeumanns27-Jan-15 21:26 
GeneralRe: My vote of 2 Pin
Erik Funkenbusch28-Jan-15 6:07
Erik Funkenbusch28-Jan-15 6:07 
GeneralRe: My vote of 2 Pin
Erik Funkenbusch7-Feb-15 14:45
Erik Funkenbusch7-Feb-15 14:45 
General[My vote of 1] Bad advice - wrong solution for the problem. Pin
mrcellux25-Jan-15 9:03
mrcellux25-Jan-15 9:03 
GeneralRe: [My vote of 1] Bad advice - wrong solution for the problem. Pin
ChristianNeumanns27-Jan-15 21:13
mvaChristianNeumanns27-Jan-15 21:13 
GeneralRe: [My vote of 1] Bad advice - wrong solution for the problem. Pin
mrcellux28-Jan-15 1:08
mrcellux28-Jan-15 1:08 
QuestionThis is absurd.. Pin
Timothy R9-Jan-15 15:14
Timothy R9-Jan-15 15:14 
AnswerRe: This is absurd.. Pin
ChristianNeumanns9-Jan-15 21:20
mvaChristianNeumanns9-Jan-15 21:20 
GeneralRe: This is absurd.. Pin
Timothy R10-Jan-15 5:56
Timothy R10-Jan-15 5:56 
ChristianNeumanns wrote:
I never said the opposite (or at least I didn't intend to say the opposite). We can think of null as 'the absence of data', and this is a valuable piece of information. A function that returns 'null' tells us that there are ... well, no data, which is a result (e.g. a function that returns null for a delivery date tells us that the delivery date is unknown, and this is valuable information).

Again, NULL is not the absence of data. It is like I said earlier, a pointer with no destination. This can easily be used to know that we do not yet know an answer, but it does not definitively tell us that the result was that there is nothing, especially for lists.

ChristianNeumanns wrote:
Could you tell us why you think that the concept of null in a DB is different from null in code?

NULL in programming is a pointer that has no destination. NULL in a DB is a special value specifically reserved to indicate "I don't know!". A null BirthDate in the DB for a customer, for example, indicates we don't know the birthdate. Obviously, there is a birthdate (otherwise wouldn't be a customer) but *we don't know it*. You can use the same concept programmatically if you so choose, (To treat a NULL pointer/value as unknown), and I often do. However it is important to understand at the root of it all, NULL in code simply means uninitialized.


ChristianNeumanns wrote:
The correlation is this: Null in code typically means that there is no list in real life. For example, if variable to_do_list points to null it means that there is no to do list (or the list is unknown).

AHA! Your starting to see the issue! Which is it, no to-do list, an unkown to-do list, or a now empty to-do list. You have lost that expressiveness by misusing NULL.

ChristianNeumanns wrote:
Sorry, but I fully disagree. A variable that points to null is initialized (because it points to null). And the compiler should track this. An uninitialized variable cannot be used in an expression, but a variable that points to null can very well be used. That's why the following Java code compiles:

First of all, there are a ridiculous number of programming languages and some that do many things odd, so I wouldn't use one languages behavior as a bedrock of how things should be done anywhere but that specific language. Having said that, I hope you realize that is a code convenience feature by the JAVA compiler for local variables (since it can be ambiguous for the next person maintaining the code) that it requires local variables to be defined. Make an array of object values, and I think you will see without initializing the object pointers, they are all NULL. Moreover, I have worked in a large number of languages, most of which work that way ( most of which allow you to be ambiguous about local variables as well).


ChristianNeumanns wrote:
Sorry, but I don't get your point.

And I can't understand the null check in your example:

Sorry, not JAVA code there. Psuedo C#. The point was that later in code when you want to know the allergies you can check for NULL, and load the allergies if they have not been loaded. If we return an empty list when the person has no allergies, we can now tell the difference between "I have no allergies" and "I don't know if I have any allergies" without any additional logic, or hackish additional variables.



ChristianNeumanns wrote:
Maybe I misunderstand, but I wonder why you think I am steamrolling null. Exactly the opposite is true. That's why I chose Why we should love 'null'[^] as title for my first article.

Because you are making NULL lose syntactical meaning by overusing it.
GeneralMy vote of 1 Pin
Malte Klena9-Jan-15 7:04
Malte Klena9-Jan-15 7:04 
GeneralRe: My vote of 1 Pin
ChristianNeumanns9-Jan-15 21:07
mvaChristianNeumanns9-Jan-15 21:07 
QuestionShopping List Poor Example Pin
DevKnightlie9-Jan-15 3:24
DevKnightlie9-Jan-15 3:24 
AnswerRe: Shopping List Poor Example Pin
ChristianNeumanns9-Jan-15 21:00
mvaChristianNeumanns9-Jan-15 21:00 
GeneralMy vote of 3 Pin
DeltaEngine9-Jan-15 1:54
professionalDeltaEngine9-Jan-15 1:54 
GeneralRe: My vote of 3 Pin
ChristianNeumanns9-Jan-15 20:53
mvaChristianNeumanns9-Jan-15 20:53 
SuggestionNo links to 4th article Pin
D4rkTrick6-Jan-15 10:57
professionalD4rkTrick6-Jan-15 10:57 
GeneralMessage Closed Pin
6-Jan-15 21:09
mvaChristianNeumanns6-Jan-15 21:09 
GeneralMy vote of 2 Pin
Elrond19-Nov-14 6:34
Elrond19-Nov-14 6:34 
GeneralRe: My vote of 2 Pin
ChristianNeumanns18-Dec-14 20:43
mvaChristianNeumanns18-Dec-14 20:43 
GeneralMy vote of 2 Pin
wout de zeeuw13-Nov-14 3:54
wout de zeeuw13-Nov-14 3:54 
QuestionIt's not really about lists. Pin
wout de zeeuw13-Nov-14 3:39
wout de zeeuw13-Nov-14 3:39 
GeneralRe: It's not really about lists. Pin
PIEBALDconsult13-Nov-14 3:46
mvePIEBALDconsult13-Nov-14 3:46 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.