Moises Gamio
Moises Gamio Software Engineer. Comprehensive experience in all phases of the software development lifecycle for several economic sectors.

Problem-Solving Efficiency: Hashmap provides efficient solutions for searching

Problem-Solving Efficiency: Hashmap provides efficient solutions for searching

In this article, I am going to demonstrate the importance of knowing in detail every data structure to support business requirements in an efficient manner.

A List data structure is an ordered collection of items, where each item has a specific position or index within the list.

A HashMap is a data structure that stores key-value pairs, enabling efficient retrieval of values based on their keys. It uses a hash function to determine the storage location (or “bucket”) of each key-value pair within an underlying array, allowing for fast lookups, insertions, and deletions.

Use Case

We have an RESTful API endpoint to retrieve a list of orders placed by a given buyer.

getOrdersByBuyer

The response includes different kind of addresses per order as you see in the following schema:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
[
  {
    "orderId": 123456,
    "buyerId": 1234,
    "customerNumberId": 12,
    "supplierId": 1,
    "orderDate": "2025-05-15",
    "orderDeliveryAddress": {
      "company": "string",
      "contact": "string",
      "countryCode": "string",
    },
    "orderInvoiceAddress": {
      "company": "string",
      "contact": "string",
      "countryCode": "string",
    },
    "desiredDeliveryDate": "2025-05-16",
    "orderPositions": [
      {
        "orderPositionNumber": 1,
        "orderPositionArticle": {
          "articleId": 4531,
        },
        "orderPositionQuantity": 10,
      }
    ]
  }
]
codersite

As you know, REST Controllers normally delegate the data retrieval to backend services.

To build the body of the list of orders, the backend service calls different database functions to retrieve data. One of these calls is to recover all historical addresses per orderId.

1
2
3
4
5
6
7
8
9
10
11
12
public List<Order> listOrders(Integer buyerId) {

  List<Order> listOfOrders = getOrdersByBuyerId(buyerId);
  
  //code ommited for brevety
  
  //retrieve headers and enrich listOfOrders
  //retrieve addresses and enrich listOfOrders
  //retrieve positions and enrich listOfOrders
  
  return listOfOrders;
}

If the user requests the first 1000 orders, the backend makes only one database function call to retrieve 2000 records (2 different addresses per orderId).

1
2
3
4
{
  //retrieve addresses
  Address[] listOfAddresses = getAddresses(order1, order2, ... order1000);
}

Here is the Object model to represent an Address:

1
2
3
4
5
6
7
8
public class Address   {
  private Integer orderId;
  private Integer addressId;
  private Integer type;
  private String company;
  private String contact;
  private String countryCode;
}

The problem comes when we need to include two loops nested in the code to assign the addresses to every order.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//retrieve addresses and enrich listOfOrders
for (Order order : listOfOrders) {
  for (Address address : listOfAddresses) {
    if (order.getOrderId().equals(address.getOrderId()) {
      switch(TYPEOFADDRESS.valueOf(address.getType())) {
        case DELIVERY:
        {
          //assign address to order
          break;
        }
        case INVOICE:
        {
          //assign address to order
          break;
        }
        default:
        {
          //assign null to order
          break;
        }
      } 
    }
  }
}

That means that the performance according to Big O Notation is N x N = O(N2).


HashMap to the rescue

We need a fast lookup to iterate exactly the two addresses per order in sequential mode.

A HashMap allows us to associate values (the two addresses) with unique key (orderId). So here is our new data structure:

1
HashMap<Integer, List<Address>> hashMapOfAddressesByOrderId = new HashMap<>();

We implement an algorithm to transform a List into a HashMap.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private HashMap<BigDecimal, List<Address>> transformListToHashMap(Address[] listOfAddresses) {

  HashMap<BigDecimal, List<Address>> hashMapOfAddressesByOrderId = new HashMap<>();
		
  for (Address address : listOfAddresses) {
    List<Address> listOfAddressesInHashMap = hashMapOfAddressesByOrderId.get(address.getOrderId());
    if (listOfAddressesInHashMap == null) {
        listOfAddressesInHashMap = new ArrayList<>();
        listOfAddressesInHashMap.add(address);
    } else {
        listOfAddressesInHashMap.add(address);
    }
    hashMapOfAddressesByOrderId.put(address.getOrderId(), listOfAddressesInHashMap);
  }

  return hashMapOfAddressesByOrderId;
}

Here is our new iteration through the new data structures.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//retrieve addresses and enrich listOfOrders

HashMap<Integer, List<Address>> hashMapOfAddressesByOrderId = transformListToHashMap(listOfAddresses);

for (Order order : listOfOrders) {

  List<Address> listOfAddressesInHashMap = hashMapOfAddressesByOrderId.get(order.getOrderId());
  
  for (Address address : listOfAddressesInHashMap) {

    switch(TYPEOFADDRESS.valueOf(address.getType())) {
      case DELIVERY:
      {
        //assign address to order
        break;
      }
      case INVOICE:
      {
        //assign address to order
        break;
      }
      default:
      {
        //assign null to order
        break;
      }
    } 
  }
}

Analysis of the algorithm performance:

first iteration: 1000N -> N

second iteration: 2 * 1000N = 2000N -> N

Total = N + N = 2N

That means that the new performance according to Big O Notation is lineal -> O(N).

The main idea in Analysis of Algorithms is always to improve the algorithm performance, by reducing the number of steps and comparisons.. – codersite.dev

java interview

When to use each:

Use a List when:

  • You care about the order of elements.

  • You need to store duplicates.

  • You iterate over elements in a sequential manner.

Use a HashMap when:

  • You need to associate values with unique keys.

  • You need fast lookup, insertion, or deletion by key.

  • The data is best represented in a key-value format.

Please support me as a writer. Every contribution helps, and your donation can help add more articles to this website, no matter how small. Thank you!