写一个hashmap

jdk的hashmap,当然比较完美,这里写一个简单的hashmap。

第一版本:

1、hash算法没有hashmap好。
2、数组长度没有做到2的n次方。
3、没有jdk8的红黑树。
4、扩容算法,直接粗暴的重新放到新数组,做了一些无用功,比如,扩容放元素的时候,是不需要比较的,可以直接放置在链表的头部,不需要一个一个遍历比较,然后放到尾部。
4、等等。

public class DiyHashMap {

    private DiyHashMap.Node[] nodes;


    private int size;

    private Integer threshold;

    private Float rate;

    private Integer length;


    public DiyHashMap(Integer length, Float rate) {
        this.length = length;
        this.rate = rate;
        Float temp = length * rate;
        this.threshold = temp.intValue();
    }


    private class Node {
        private K key;
        private V value;
        private Node next;

        public Node(K key, V value) {
            this.key = key;
            this.value = value;
        }
    }

    public void put(K k, V v) {
        if (nodes == null) {
            nodes = new DiyHashMap.Node[length];
        }
        if (size >= threshold) {
            // 扩容
            resize();
        }
        Node node = new Node(k, v);
        int index = indexOfArray(k.hashCode());
        if (nodes[index] == null) {
            nodes[index] = node;
            size++;
        } else {
            // 链表查找
            Node n = nodes[index];
            while (true) {
                if (n.next == null) {
                    // 直接队列尾放置
                    n.next = node;
                    size++;
                    break;
                } else if (n.next.key.hashCode() == k.hashCode() && n.next.key.equals(k)) {
                    // 替换
                    n.next.value = v;
                    break;
                } else {
                    // 往下遍历
                    n = n.next;
                }
            }
        }
    }

    public void resize() {
        int newLength = length << 1;
        this.length = newLength;
        Float temp = length * rate;
        this.threshold = temp.intValue();
        this.size = 0;
        Node[] oldNodes = nodes;
        DiyHashMap.Node[] newNodes = new DiyHashMap.Node[newLength];
        this.nodes = newNodes;

        for (int i = 0; i < oldNodes.length; i++) {
            Node oldNode = oldNodes[i];
            while (true) {
                if (oldNode != null) {
                    this.put(oldNode.key, oldNode.value);
                    oldNode = oldNode.next;
                } else {
                    break;
                }
            }
        }

    }

    public int indexOfArray(int hashcode) {
        int i = hashcode % length;
        if(i < 0){
            return 0 - i;
        }
        return i;
    }

    public V get(K k) {
        int index = indexOfArray(k.hashCode());
        Node node = nodes[index];
        while (true) {
            if (node == null) {
                return null;
            } else if (node.key.hashCode() == k.hashCode() && node.key.equals(k)) {
                // 找到了
                return node.value;
            } else {
                node = node.next;
            }
        }
    }
}

测试:正确

 public static void main(String[] args) {
        DiyHashMap map = new DiyHashMap<>(2, 0.75f);
        String key = "hello";
        for(int i = 0; i < 10000; i++){
            map.put(key + i, "world" + i);

        }

        for(int i = 0; i < 10000; i++){
            String value = map.get(key + i);
            if(!value.equals("world" + i)){
                System.out.println("fail");
            }
        }
    }

再来一个版本:
1、优化了一下扩容。
2、因为2的n次方,需要比较麻烦的位运算,所以除非复制hashmap的代码,否则,手写不出来。。
2的n次方写不出,那么取模运算,也就优化不了。
2的n次方写不出来,就不能用高位,去推算扩容后的位置,所以扩容后也就只能链表倒置了。
3、红黑树太复杂了,也不写。

public class DiyHashMapV2 {
    private DiyHashMapV2.Node[] nodes;
    private int size;
    private Integer threshold;

    private Float rate;

    private Integer length;


    public DiyHashMapV2(Integer length, Float rate) {
        this.length = length;
        this.rate = rate;
        Float temp = length * rate;
        this.threshold = temp.intValue();
    }


    private class Node {
        private K key;
        private V value;
        private Node next;

        public Node(K key, V value) {
            this.key = key;
            this.value = value;
        }
    }

    public void put(K k, V v) {
        if (nodes == null) {
            nodes = new DiyHashMapV2.Node[length];
        }
        if (size >= threshold) {
            // 扩容
            resize();
        }
        Node node = new Node(k, v);
        int index = indexOfArray(k.hashCode());
        if (nodes[index] == null) {
            nodes[index] = node;
            size++;
        } else {
            // 链表查找
            Node n = nodes[index];
            while (true) {
                if (n.next == null) {
                    // 直接队列尾放置
                    n.next = node;
                    size++;
                    break;
                } else if (n.next.key.hashCode() == k.hashCode() && n.next.key.equals(k)) {
                    // 替换
                    n.next.value = v;
                    break;
                } else {
                    // 往下遍历
                    n = n.next;
                }
            }
        }
    }
// 就重写了一下扩容方法
    public void resize() {
        int newLength = length << 1;
        this.length = newLength;
        Float temp = length * rate;
        this.threshold = temp.intValue();
        Node[] oldNodes = nodes;
        DiyHashMapV2.Node[] newNodes = new DiyHashMapV2.Node[newLength];
        this.nodes = newNodes;
        for (int i = 0; i < oldNodes.length; i++) {
            Node oldNode = oldNodes[i];
            while (true) {
                if (oldNode != null) {
                    int newIndex = indexOfArray(oldNode.key.hashCode());
                    // 新数组的这个位置是否有值
                    if (null == newNodes[newIndex]) {
                        newNodes[newIndex] = oldNode;
                        oldNode = oldNode.next;
                        // 这里需要把以前的next指标清空,否则,因为,扩容后,链表倒置了,会造成链表循环next,也就是链表的最后一个元素的next会指向链表的第一个元素
                        newNodes[newIndex].next = null;
                    } else {
                        // 直接放到头部
                        Node tmp = oldNode.next;
                        Node t = newNodes[newIndex];
                        newNodes[newIndex] = oldNode;
                        oldNode.next = t;
                        oldNode = tmp;
                    }
                } else {
                    break;
                }

            }

        }
    }

    public int indexOfArray(int hashcode) {
        int i = hashcode % length;
        if(i < 0){
            return 0 - i;
        }
        return i;
//        return 1;
    }

    public V get(K k) {
        int index = indexOfArray(k.hashCode());
        Node node = nodes[index];
        while (true) {
            if (node == null) {
                return null;
            } else if (node.key.hashCode() == k.hashCode() && node.key.equals(k)) {
                // 找到了
                return node.value;
            } else {
                node = node.next;
            }
        }
    }
}

测试一下性能:
测试结果其实性能差不多。
测试的结果不是很精确,因为每次都不一样,跟,cpu的波动,分配的执行时间有关。

public static void main(String[] args) {
        String key = "hello";
        DiyHashMap map = new DiyHashMap<>(16, 0.75f);
        for (int i = 0; i < 5000000; i++) {
            map.put(key + i, "world" + i);
        }

        long start = System.nanoTime();
        DiyHashMap map1 = new DiyHashMap<>(16, 0.75f);
        for (int i = 0; i < 5000000; i++) {
            map1.put(key + i, "world" + i);

        }
        /*
测试,能不能取到元素
for (int i = 0; i < 10000; i++) {
            String value = map.get(key + i);
            if (!value.equals("world" + i)) {
                System.out.println("fail");
            }
        }*/
        long end = System.nanoTime();
        DiyHashMapV2 map2 = new DiyHashMapV2<>(16, 0.75f);
        for (int i = 0; i < 5000000; i++) {
            map2.put(key + i, "world" + i);
        }
        long end2 = System.nanoTime();

        Map map3 = new HashMap<>(16, 0.75f);
        for (int i = 0; i < 5000000; i++) {
            map3.put(key + i, "world" + i);
        }
        long end3 = System.nanoTime();
        System.out.println(end - start);
        System.out.println(end2 - end);
        System.out.println(end3 - end2);

   }

3128620097
5787549157
3656479197

你可能感兴趣的:(写一个hashmap)