Hash Table with Chaining

medium Java Collections

Problem

Every input line is a command: PUT key value, GET key, DELETE key or SIZE. Keys and values are words without spaces. PUT prints new (key was not there) or updated. GET prints the value or none. DELETE prints deleted or none. SIZE prints how many keys are stored. Build the table yourself: a fixed array of 16 buckets, where each bucket is a list of key-value pairs (chaining). The bucket is the hash of the key modulo 16.

Constraints

  • 1 <= lines <= 10^4
  • keys and values are words without spaces

Examples

Input

PUT a 1
GET a
PUT a 5
GET a
DELETE a
GET a

Output

new
1
updated
5
deleted
none

Solutions

AlternativeTime O(n * chain length)Space O(n)

Keys that land in the same bucket share a short list, so a search scans only that list. With a good hash and 16 buckets the lists stay short. This is how a real HashMap is built, without resizing.

Same code works in Java810172125Basic features only, so nothing changes between versions.
/*
 * Problem : Process lines.
 * Input   : Several lines.
 * Output  : One result per command.
 * Approach: Own table with chaining.
 * Time    : O(n * chain length)
 * Space   : O(n)
 * Java    : 8 or newer.
 */
import java.util.Scanner;

public class Main {
        // Step 1: A fixed array of buckets. Each bucket is a list of {key, value} pairs that share the same hash.
        static class Table {
            private final java.util.List<java.util.List<String[]>> buckets = new java.util.ArrayList<java.util.List<String[]>>();
            private int size = 0;

            Table() {
                for (int i = 0; i < 16; i++) {
                    buckets.add(new java.util.ArrayList<String[]>());
                }
            }

            // Step 2: The hash turns a key into a bucket number.
            private int bucketOf(String key) {
                long hash = 0;
                for (int i = 0; i < key.length(); i++) {
                    hash = (hash * 31 + key.charAt(i)) % 1000003;
                }
                return (int) (hash % 16);
            }

            private String[] find(String key) {
                for (String[] pair : buckets.get(bucketOf(key))) {
                    if (pair[0].equals(key)) {
                        return pair;
                    }
                }
                return null;
            }

            String put(String key, String value) {
                String[] pair = find(key);
                if (pair != null) {
                    pair[1] = value;
                    return "updated";
                }
                buckets.get(bucketOf(key)).add(new String[] {key, value});
                size++;
                return "new";
            }

            String get(String key) {
                String[] pair = find(key);
                return pair == null ? "none" : pair[1];
            }

            String delete(String key) {
                String[] pair = find(key);
                if (pair == null) {
                    return "none";
                }
                buckets.get(bucketOf(key)).remove(pair);
                size--;
                return "deleted";
            }

            int size() {
                return size;
            }
        }

    // This method solves the task. It receives the input and returns the answer.
    static String[] solve(String[] lines) {
        Table table = new Table();
        String[] output = new String[lines.length];
        for (int i = 0; i < lines.length; i++) {
            String[] part = lines[i].split(" ");
            if (part[0].equals("PUT")) {
                output[i] = table.put(part[1], part[2]);
            } else if (part[0].equals("GET")) {
                output[i] = table.get(part[1]);
            } else if (part[0].equals("DELETE")) {
                output[i] = table.delete(part[1]);
            } else {
                output[i] = String.valueOf(table.size());
            }
        }
        return output;
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        java.util.List<String> all = new java.util.ArrayList<>();
        while (scanner.hasNextLine()) {
            String line = scanner.nextLine().trim();
            if (!line.isEmpty()) {
                all.add(line);
            }
        }
        String[] lines = all.toArray(new String[0]);
        System.out.println(String.join("\n", solve(lines)));
    }
}
Hints (2)
  1. What happens when two keys have the same bucket?
  2. Why does PUT first call find()?