简体   繁体   中英

Custom HashMap implementation

This question was asked to me in an interview. I think the only way to get best solution is SOF. So the question was " How would you implement a custom HashMap in java(Assume there is no such data structure called HashMap) ". The only answer I could think of was by implementing Associative Arrays (but then again, Java doesnt have associative arrays). Could you experts please pour in your thoughts on this question?

Short Answer:

It would be an array of arrays (or Lists).

Object[][] map;

where map[bucketIndex] would return the 'bucket'.

When inserting something you need a function to calculate bucketIndex this would use the hashcode of the inserted object.

Boom done!

:)

References: - http://javarevisited.blogspot.sg/2011/10/override-hashcode-in-java-example.html - http://javarevisited.blogspot.in/2011/02/how-to-write-equals-method-in-java.html

class Entry<K, V> {
    K key;
    V val;

    public K getKey() {
        return key;
    }

    public void setKey(K key) {
        this.key = key;
    }

    public V getVal() {
        return val;
    }

    public void setVal(V val) {
        this.val = val;
    }

    @Override
    public int hashCode() {
        int prime = 13;
        int mul = 11;
        if (key != null) {
            int hashCode = prime * mul + key.hashCode();
            return hashCode;
        }
        return 0;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || this.getClass().getName() != o.getClass().getName()) {
            return false;
        }
        Entry e = (Entry) o;
        if (this.key == e.key) {
            return true;
        }
        return false;
    }
}

public class HashMapImpl<K, V> {
    private float loadfactor = 0.75f;
    private int capacity = 100;
    private int size = 0;
    private Entry<K, V> table[] = new Entry[capacity];

    private int Hashing(int hashCode) {
        int location = hashCode % capacity;
        System.out.println("Location:"+location);
        return location;
    }

    public int size() {
        // TODO Auto-generated method stub
        return this.size;
    }

    public boolean isEmpty() {
        if(this.size == 0) {
            return true;
        }
        return false;
    }

    public boolean containsKey(Object key) {
        if(key == null) {
            if(table[0].getKey() == null) {
                return true;
            }
        }
        int location = Hashing(key.hashCode());
        Entry e = null;
        try {
            e = table[location];
        }catch(NullPointerException ex) {

        }
        if(e!= null && e.getKey() == key) {
            return true;
        }
        return false;
    }

    public boolean containsValue(Object value) {
        for(int i=0; i<table.length;i++) {
            if(table[i] != null && table[i].getVal() == value) {
                return true;
            }
        }
        return false;
    }

    public V get(K key) {
        V ret = null;
        if(key == null) {
            Entry<K, V> e = null;
            try{
                e = table[0];
            }catch(NullPointerException ex) {

            }
            if(e != null) {
                return (V) e.getVal();
            }
        } else {
            int location = Hashing(key.hashCode());
            Entry<K, V> e = null;
            try{
            e = table[location];
            }catch(NullPointerException ex) {

            }
            if(e!= null && e.getKey() == key) {
                return (V)e.getVal();
            }
        }
        return ret;
    }

    public V put(K key, V val) {
        V ret = null;
        if (key == null) {
            ret = putForNullKey(val);
            return ret;
        } else {
            int location = Hashing(key.hashCode());
            if(location >= capacity) {
                System.out.println("Rehashing required");
                return null;
            }
            Entry<K, V> e = null;
            try{
            e = table[location];
            }catch(NullPointerException ex) {

            }
            if (e!= null && e.getKey() == key) {
                ret = (V) e.getVal();
            } else {
                Entry<K, V> eNew = new Entry<K,V>();
                eNew.setKey(key);
                eNew.setVal(val);
                table[location] = eNew;
                size++;
            }
        }
        return ret;
    }

    private V putForNullKey(V val) {
        Entry<K, V> e = null;
        try {
            e = table[0];
        }catch(NullPointerException ex) {

        }
        V ret = null;
        if (e != null && e.getKey() == null) {
            ret = (V) e.getVal();
            e.setVal(val);
            return ret;
        } else {
            Entry<K, V> eNew = new Entry<K,V>();
            eNew.setKey(null);
            eNew.setVal(val);
            table[0] = eNew;
            size++;
        }
        return ret;
    }

    public V remove(K key) {
        int location = Hashing(key.hashCode());
        V ret = null;
        if(table[location].getKey() == key) {
            for(int i=location; i<table.length;i++) {
                table[i] = table[i+1];
            }
        }
        return ret;
    }

    public static void main(String[] args) {
        HashMapImpl<Integer, String> hashMap = new HashMapImpl<Integer, String>();
        hashMap.put(10, "Apple");
        hashMap.put(1, "Orange");
        hashMap.put(79, "Grape");
        System.out.println("Val at 79 "+hashMap.get(79));
        System.out.println("Val at 1 "+hashMap.get(1));
        System.out.println("Val at 10 "+hashMap.get(10));
        System.out.println("Val at 2 "+hashMap.get(2));
        hashMap.put(null, "Pear");
        System.out.println("Val at null "+hashMap.get(null));
        System.out.println("Hashmap has key at null:"+hashMap.containsKey(null));
        System.out.println("Hashmap has value at null:"+hashMap.containsValue("Pear"));
        System.out.println("Size of Map:"+hashMap.size());
    }


}
public class ArrayAsHashMap {

    Object [][] hashArr  = new Object [10] [2];

    public void put(HashObject key, String value){
        int index = key.hashCode();
        Object [] arr = {key, value};
        hashArr[index] = arr;
    }

    public String get(HashObject key){
        int index = key.hashCode();
        Object [] arr = hashArr[index];
        return (String)arr[1];

    }        

}

Look at Cliff Click's nonblockinghahmap for an example of a need for a hashmap implemented in java. Remember that an associated array is just another name for a hash map, so he's asking you how to implement it.

Generally hashes are implemented using standard arrays (not lists or anything fancy for speed). The issue is what is inside each of these arrays... in the case of a hash collision do you want to use a LinkedList (chaining) or do you want to rehash and go to another array location (open addressing). Read some more computer science to learn the cost/benefit to doing both.

adding just one more approach - how about if we use set instead of hashMap in this way

Create a custom class called pair -

public class Pair  {

    private String key;
    private Object Value;

    public Pair(String key, Object value) {
        this.key = key;
        Value = value;
    }

    public String getKey() {
        return key;
    }

    public Object getValue() {
        return Value;
    }


    @Override
    public int hashCode() {
        return key.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Pair other = (Pair) obj;
        if (key != other.getKey())
            return false;
        return true;
    }
}

and then you can directly use java Set as Hashmap

 public static void main(String[] args) {

        Set<Pair> set = new HashSet<Pair>();

        set.add(new Pair("key1", "val1"));
        set.add(new Pair("key2", "val2"));
        set.add(new Pair("key1", "val3"));

        // you can advanced for loop for looping over set
        for (Pair pair : set) {

        }

        // with java 8 - you can also use streams over it

       // set.stream().filter
       // set.stream().anyMatch
        // and some more methods can be directly used


    }

With Java-8 Stream API - it will enable this to make some more operations easy and fast.

Just adding this as one more possible approach for custom hashmap -

Whenever there is no value present at a particular index, we will directly place the Entry object at that index. Else, we will traverse the LinkedList until we reach the last entry of the list and place the new entry object as the next node of the last entry object. In the process, if we find the key already exists then we simply replace its value with the new one.

public void put(K key, V value){
        int index = index(key);
        Entry newEntry = new Entry(key, value, null);
        if(table[index] == null){
            table[index] = newEntry;
        }else {
            Entry previousNode = null;
            Entry currentNode = table[index];
            while(currentNode != null){
                if(currentNode.getKey().equals(key)){
                    currentNode.setValue(value);
                    break;
                }
                previousNode = currentNode;
                currentNode = currentNode.getNext();
            }
            if(previousNode != null)
                previousNode.setNext(newEntry);
            }
    }
     public class Entry {
        protected final Object key;
        protected Object value;
        protected Entry next;
    
        public Entry(Object k, Object v) {
            this.key = k;
            this.value = v;
        }
    
        public void setValue(Object v) {
            this.value = v;
        }
    
        public Object getValue() {
            return this.value;
        }
    
        public Object getKey() {
            return this.key;
        }
    
        @Override
        public String toString() {
            return "Entry [key=" + key + ", value=" + value + ", next=" + next + "]";
        }
    }



public class OwnHashMap {

    private static final int SIZE = 16;

    private Entry tables[] = new Entry[SIZE];

    public Entry get(Object k) {
        int hash = k.hashCode() % SIZE;
        hash=Math.abs(hash);
        Entry e = tables[hash];

        if (e != null) {
            while (e.getKey().equals(k)) {
                return e;
            }
            e = e.next;

        }
        return null;
    }

    public void put(Object k, Object v) {
        int hash = k.hashCode() % SIZE;
        hash=Math.abs(hash);
        Entry e = tables[hash];
        if (e != null) {

            if (e.getKey().equals(k)) {
                e.value = v;
            } else {

                while (e.next != null) {
                    e = e.next;
                }
                Entry entryOld = new Entry(k, v);
                tables[hash] = entryOld;
            }

        } else {
            Entry entryNew = new Entry(k, v);
            tables[hash] = entryNew;
        }
    }
    
    public void printMap() {
         for(Entry m:tables) {
              if(m!=null)
                   System.out.println(m.getKey()+"  "+m.getValue());
         }
    }

    public static void main(String[] args) {
        OwnHashMap map = new OwnHashMap();
        map.put("Vikas", 10);
        map.put("Govind", 1);
        map.put("Abhishek", 3);
        map.put("Sandeep", 4);
    

        map.printMap();

    }

}

Output

Govind  1
Sandeep  4
Abhishek 3
Vikas  10
Simple HashMap Implementation in java

// Implementation class
public class HashMapCustom<K, V>
{
    static class Entry <K,V>{
        K key;
        V value;
        Entry<K,V> next;
        public  Entry(K key, V value,Entry<K,V> next ){
            this.key=key;
            this.value=value;
            this.next=next;
        }
    }

    private Entry<K,V>[] bucket ;
    private int capacity = 4;

    public  HashMapCustom(){
        bucket = new Entry [capacity];
    }

    public void put(K key, V value){
        if(null == key) return; // null keys not allowed
        int hash = hash(key);
        Entry<K,V> add = new Entry<K,V> (key,value,null);
        if(bucket[hash]==null){
            bucket[hash]=add;
        }else{
            Entry<K,V> curr = bucket[hash];
            while(null !=curr){
                if(curr.key.equals (key)){
                    curr.value =value;
                    return;
                }else{
                   if(curr.next == null){
                       curr.next = add;
                       return;
                   }
                   curr = curr.next;
                }
            }
        }
    }
    public V get(K key){
        if(null == key) return null;
        int hash = hash(key);
        Entry<K,V> b = bucket[hash];
        if(null == b) return null;
        while(null !=b){
            if(b.key.equals (key)){
                return b.value;
            }else{
                b=b.next;
            }
        }
        return null;
    }
    public boolean remove(K key){
        if(null == key) return false;
        int hash = hash(key);
        Entry<K,V> b = bucket[hash];
        if(null == b) return false;
        Entry<K,V> prev = null;
        while(null !=b){
            if(b.key.equals (key)){
                //delete
               if(prev == null) {  // first node to remove
                bucket[hash]=b.next;
                return true;
               }else{
                   prev.next = b.next;
                   return true;
               }
            }else{
                prev=b;
                b =b.next;
            }
        }
        return false;
    }
    private int hash (K key)
    {
        return Math.abs (key.hashCode ())%capacity;
    }
}

// calling class
public class App
{
    public static void main(String a[]){
        HashMapCustom<Integer,Integer> map = new HashMapCustom<Integer,Integer> ();
        map.put (1,99);
        map.put (2,24);
        map.put (6,32);
        map.put (4,10);
        map.put (10,12);
        map.remove (2);
        map.put (1,44);
        System.out.println ( map.get (6));
        System.out.println ( map.get (2));
        System.out.println ( map.get (6));
        System.out.println ( map.get (1));

    }
}

You should use one dimensional array. Object[] arr.

The index of the array is the normalized hashcode from the Key object. The array holds the Pair.

The reason the value consists of both Key and Value is that if there is any hash collision , it has to go thru the list of keys inside the slot and find out which is the correct key (using equals on the key object).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM