Enhanced Hashmap - Add a number to all keys/values I had the below problem in a coding test and I got 28/30 tests passes and 2 failed due to a time-out.

Problem You have created a programming language and now you have decided to add hashmap support to it. It was found that in
common programming languages, it is impossible to add a number to all
hashmap keys/values. So, you have decided to implement your own
hashmap in your new language with following operations.

insert x y - insert and object with key x and value y
get x - return the value of an object with key x
addToKey x - add x to all keys in map
addToValue y - add y to all values in map

Your task is to implement this hashmap, apply the given queries, and
to find the sum of all the results for get operations

For Example

For queryType=[&quot;insert&quot;,&quot;insert&quot;,&quot;addToValue&quot;,&quot;addToKey&quot;,&quot;get&quot;] and query=[[1,2],[2,3],[2],[1],[3]], the output should be hashMap(queryType,query)=5.

Explanation

insert 1 2 - hashmap will be {1:2}
insert 2 3 - hashmap will be {1:2,2:3}
addToValue 2 - hashmap will be {1:4,2:5}
addToKey 1 - hashmap will be {2:4,3:5}
get 3 - value is 5


Input/Output

[execution time limit] 3 seconds (Java)
[input] array.string queryType
Array of query types. its is guaranteed that each queryType[i] any one of the above mentioned operation
1&lt;=queryType.length&lt;=10^5
[input] array.array.integer query
Array of queries, where each query is mentioned by 2 numbers for insert and one number for others
Key values are in range [-10^9,10^9]


Below is my solution in Java
long hashMap(String[] queryType, int[][] query) {
        long sum = 0;
        Integer currKey = 0;
        Integer currValue = 0;
        Map&lt;Integer, Integer&gt; values = new HashMap&lt;&gt;();
        for (int i = 0; i &lt; queryType.length; i++) {
            String currQuery = queryType[i];
            switch (currQuery) {
            case &quot;insert&quot;:
                HashMap&lt;Integer, Integer&gt; copiedValues = new HashMap&lt;&gt;();
                if (currKey != 0 || currValue != 0) {
                    Set&lt;Integer&gt; keys = values.keySet();
                    for (Integer key : keys) {
                        copiedValues.put(key + currKey, values.get(key) + currValue);
                    }
                    values.clear();
                    values.putAll(copiedValues);
                    currValue = 0;
                    currKey = 0;
                }
                values.put(query[i][0], query[i][1]);
                break;
            case &quot;addToValue&quot;:
                currValue += values.isEmpty() ? 0 : query[i][0];
                break;
            case &quot;addToKey&quot;:
                currKey += values.isEmpty() ? 0 : query[i][0];
                break;
            case &quot;get&quot;:
                copiedValues = new HashMap&lt;&gt;();
                if (currKey != 0 || currValue != 0) {
                    Set&lt;Integer&gt; keys = values.keySet();
                    for (Integer key : keys) {
                        copiedValues.put(key + currKey, values.get(key) + currValue);
                    }
                    values.clear();
                    values.putAll(copiedValues);
                    currValue = 0;
                    currKey = 0;
                }
                sum += values.get(query[i][0]);
            }
        }
        return sum;
    }

Is there any other data structure I can use instead of hashmap or Can I improve my code to be more linear?
I would suggest you create your own OffsetIntegerMap that can map between integers and handle an offset on the keys and values.
You don't necessarily have to implement the HashMap from scratch, define your own limited interface and implement it with an existing Map&lt;Integer, Integer&gt; through composition.
By handling the offsets separately from the keys and values the complexity of the offset operations end up O(1) instead of O(n) when doing recalculations and the Map&lt;&gt; put and get operations stay at their original O(1).
An example an &quot;OffsetIntegerMap&quot;:
import java.util.HashMap;
import java.util.Map;

public class OffsetIntegerMap {
    private final Map&lt;Integer, Integer&gt; actualMap;
    private int keyOffset = 0;
    private int valueOffset = 0;

    public OffsetIntegerMap() {
        actualMap = new HashMap&lt;&gt;();
    }

    public int size() {
        return actualMap.size();
    }

    public boolean isEmpty() {
        return actualMap.isEmpty();
    }

    public boolean containsKey(int key) {
        var keyWithoutOffset = key - keyOffset;
        return actualMap.containsKey(keyWithoutOffset);
    }

    public boolean containsValue(int value) {
        var valueWithoutOffset = value - valueOffset;
        return actualMap.containsValue(valueWithoutOffset);
    }

    public Integer get(int key) {
        var keyWithoutOffset = key - keyOffset;
        var value = actualMap.get(keyWithoutOffset);
        if (value == null) return null;
        return value + valueOffset;
    }

    public Integer put(int key, int value) {
        var keyWithoutOffset = key - keyOffset;
        var valueWithoutOffset = value - valueOffset;
        var oldValue = actualMap.put(keyWithoutOffset, valueWithoutOffset);
        if (oldValue == null) return null;
        return oldValue + valueOffset;
    }

    public Integer remove(int key) {
        var keyWithoutOffset = key - keyOffset;
        var oldValue = actualMap.remove(keyWithoutOffset);
        if (oldValue == null) return null;
        return oldValue + valueOffset;
    }

    public void clear() {
        actualMap.clear();
        keyOffset = 0;
        valueOffset = 0;
    }

    public int getKeyOffset() {
        return keyOffset;
    }

    public void setKeyOffset(int keyOffset) {
        this.keyOffset = keyOffset;
    }

    public int getValueOffset() {
        return valueOffset;
    }

    public void setValueOffset(int valueOffset) {
        this.valueOffset = valueOffset;
    }

    public void addToValues(int toAdd) {
        this.valueOffset += toAdd;
    }

    public void addToKeys(int toAdd) {
        this.keyOffset += toAdd;
    }
}

By encapsulating the offset logic the processing loop also becomes much simpler without refactoring much of anything:
static long hashMap(String[] queryType, int[][] query) {
    long sum = 0;
    var map = new OffsetIntegerMap();
    for (int i = 0; i &lt; queryType.length; i++) {
        String currQuery = queryType[i];
        switch (currQuery) {
            case &quot;insert&quot;:
                map.put(query[i][0], query[i][1]);
                break;
            case &quot;addToValue&quot;:
                map.addToValues(query[i][0]);
                break;
            case &quot;addToKey&quot;:
                map.addToKeys(query[i][0]);
                break;
            case &quot;get&quot;:
                sum += map.get(query[i][0]);
        }
    }
    return sum;
}