简体   繁体   English

自定义 HashMap 实现

[英]Custom HashMap implementation

This question was asked to me in an interview.这个问题是在一次采访中问我的。 I think the only way to get best solution is SOF.我认为获得最佳解决方案的唯一方法是 SOF。 So the question was " How would you implement a custom HashMap in java(Assume there is no such data structure called HashMap) ".所以问题是“您将如何在 java 中实现自定义 HashMap(假设没有这种称为 HashMap 的数据结构) ”。 The only answer I could think of was by implementing Associative Arrays (but then again, Java doesnt have associative arrays).我能想到的唯一答案是实现关联数组(但话说回来,Java 没有关联数组)。 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'.其中map[bucketIndex]将返回“bucket”。

When inserting something you need a function to calculate bucketIndex this would use the hashcode of the inserted object.插入内容时,您需要一个函数来计算bucketIndex这将使用插入对象的hashcode

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参考资料: - 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.查看 Cliff Click 的nonblockinghahmap以了解需要在 java 中实现的 hashmap 的示例。 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).问题是这些数组中的每一个都有什么......在散列冲突的情况下,您是要使用 LinkedList(链接)还是要重新散列并转到另一个数组位置(开放寻址)。 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添加另一种方法 - 如果我们以这种方式使用 set 而不是 hashMap 怎么样

Create a custom class called pair -创建一个名为 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然后就可以直接使用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.使用 Java-8 Stream API - 它将使更多操作变得简单快捷。

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.每当特定索引处没有值时,我们将直接将 Entry 对象放置在该索引处。 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.否则,我们将遍历 LinkedList 直到到达列表的最后一个条目,并将新条目对象放置为最后一个条目对象的下一个节点。 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.对象[] arr。

The index of the array is the normalized hashcode from the Key object.数组的索引是来自 Key 对象的规范化哈希码。 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).值由 Key 和 Value 组成的原因是,如果存在任何哈希冲突,它必须遍历槽内的键列表并找出哪个是正确的键(在键对象上使用 equals)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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