4

I recently had an interview where the interviewer asked me to create a HashMap that has a maximum of 7 key/value pairs. If an 8th key/value pair is added, the first key/value pair should be removed and the eighth inserted to replace it, etc.

What's a good strategy for solving this problem?

Zabuzard
  • 25,064
  • 8
  • 58
  • 82
Tanu Garg
  • 3,007
  • 4
  • 21
  • 29
  • 2
    You need a custom structure for that, HashMap does not support this. Is there a specific reason you need a HashMap? With that small amount of elements, a circular array would yield the same or even better performance (`contains` starts to be faster using hashes after about 7 elements, versus a standard search in an array) – Zabuzard Jul 23 '19 at 18:28
  • A HashMap is *not ordered* – Thiyagu Jul 23 '19 at 18:29
  • someone asked this in an interview, and he wanted hashmap only. – Tanu Garg Jul 23 '19 at 18:29
  • While this can conceptionally be implemented without many extra steps, a HashMap, design-wise and how it is implemented in Java, does not support this. It is not limited in size and not ordered. Also, Javas HashMap is not always represented by an array table with hashed-based indices. It swaps representation to a red/black tree depending on various factors. – Zabuzard Jul 23 '19 at 18:30
  • thankyou @templatetypedef for the edits. – Tanu Garg Jul 23 '19 at 18:35
  • 1
    During a verbal interview, `HashMap` and "hash map" sound the same. Java *does* support a structure that supports both hashed access and an LRU cache replacment. – Andy Thomas Jul 23 '19 at 18:50
  • If the interviewer asked specifically that you use `HashMap` for this, then it's not a very good interview question because it's asking for a complicated solution to a simple problem. – Jim Mischel Jul 24 '19 at 15:21
  • @JimMischel depends on the point of view, as `LinkedHashMap` *is* a `HashMap` and so is a subclass of it overriding `removeEldestEntry`. That’s a simple solution, which can be passed to any code expecting a `HashMap`. – Holger Jul 25 '19 at 12:38

2 Answers2

6

The Java standard libraries contains a type called LinkedHashMap that does more or less what you're hoping to do here. It's like a regular HashMap except that it keeps track of the order in which elements were inserted. If you define a subclass and override the removeEldestEntry protected method, then the LinkedHashMap will automagically evict old elements and replace them with new ones on whatever schedule you'd like.

On the other hand, if you'd like to build something like this on your own, you're probably looking for something like a hash table that has a doubly-linked-list threaded through the elements. Whenever you insert an element, you append it to the linked list, and then, if there are too many elements, you remove the first element. I'll leave the details of how to do deletions, etc. up to you.

That being said, the above strategies are best-suited for fairly large maps (say, a hundred key/value pairs or more). If you only need to store seven key/value pairs, then it's almost certainly faster to just throw everything in an unsorted array and iterate over the elements to find the one you're looking for by just checking each. :-)

And finally, fun fact: what you're designing is sometimes called an LRU cache. These are used extensively in hardware and software.

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
  • @user7 I believe that all the solutions outlined here can support that. The `LinkedHashMap` and `HashMap` with a threaded linked list both naturally do this. The array-based one can achieve this by tracking the index of the next slot to override so that elements don't need to be explicitly moved around. – templatetypedef Jul 23 '19 at 18:36
  • Yes. There is no need to shift up. I added that comment thinking the new element needs to be added at the last. – Thiyagu Jul 23 '19 at 18:38
  • @templatetypedef that’s what `ArrayDeque` gives you for free. – Holger Jul 25 '19 at 12:40
6

Make a data structure using LinkedHashMap and override removeEldestEntry i.e. something like this:

import java.util.LinkedHashMap;

class CustomHashMap extends LinkedHashMap<Integer, Integer> {
    private int capacity;

    public CustomHashMap(int capacity) {
        super(capacity, 0.75F, true);
        this.capacity = capacity;
    }

    public int get(int key) {
        return super.getOrDefault(key, -1);
    }

    public void put(int key, int value) {
        super.put(key, value);
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
        return size() > capacity; 
    }
}

Alternatively, if you are not allowed to use standard libraries or you are using a language that does not have an ordered dictionary structure like Java/Python you can use a Hashtable + and a DoubleEndedLinkedList that you can define yourself and achieve the same thing or use a Deque:

  • Time complexity: O(1) for both put and get.
  • Space complexity: O(capacity).

Although you have to write a lot more code.


Generic version as per @Holger's request:

import java.util.LinkedHashMap;
import java.util.Map;

class CustomHashMap<K, V> extends LinkedHashMap<K, V> {
    private int capacity;

    public CustomHashMap(int capacity) {
        super(capacity, 0.75F, true);
        this.capacity = capacity;
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
        return size() > capacity;
    }
}

Example Usage:

class Main {
    public static void main(String[] args) {
        CustomHashMap map = new CustomHashMap(3);
        map.put(1, null);
        map.put(2, null);
        map.put(3, null);
        map.put(4, null);
        System.out.println(map.keySet());
    }
}

Output:

[2, 3, 4]
Sash Sinha
  • 18,743
  • 3
  • 23
  • 40
  • Just as a minor nite: While this might indeed be an elegant solution, I think thats not really what the interviewer was waiting for. Its very specific and the interview was likely more about how to do it conceptionally, not really talking about Javas HashMap class in the first place. – Zabuzard Jul 23 '19 at 18:46
  • I was asked this question at a couple of interviews last year :) Some interviewers will move on to the next question or ask you to come up with your own LinkedList implementation. – Sash Sinha Jul 23 '19 at 18:46
  • I somehow missed the part of the question where it says that it has to be a `Map` and should have extra methods for `int`… – Holger Jul 25 '19 at 12:25
  • @Holger There you go. – Sash Sinha Jul 25 '19 at 13:16