每日一省之—使用线性探测法(仅利用数组作为底层数据结构)实现HashMap

看这篇文章前,不妨看我的上一篇文章:每日一省之————利用数组和链表实现一个简单的HashMap。这样一来,相信大家对Java中的HashMap会有一个更全面的认识。好了,废话不多说了,直接上代码吧:


import java.util.LinkedList;
import java.util.Queue;

public class LinearProbingHashMap<K, V> {

    public static final int INITIAL_CAPACITY = 4;

    private int N;
    private int M;
    private K[] keys;
    private V[] values;

    public LinearProbingHashMap(int arrayLength) {
        this.M = arrayLength;
        keys = (K[]) new Object[M];
        values = (V[]) new Object[M];
    }

    public LinearProbingHashMap() {
        this(INITIAL_CAPACITY);
    }

    public int size() {
        return N;
    }

    public boolean isEmpty() {
        return size() == 0;
    }

    public boolean contains(K key) {
        if (key == null)
            throw new NullPointerException("参数不能为null");
        return get(key) != null;
    }

    private int hash(K key) {
        return (key.hashCode() & 0x7fffffff) % M;
    }

    /*
     * 不扩容会导致无限循环,why?大家自己思考哦
     */
    private void resize(int arrayLength) {
        LinearProbingHashMap<K, V> temp = new LinearProbingHashMap<K, V>(arrayLength);
        for (int i = 0; i < M; i++) {
            if (keys[i] != null) {
                temp.put(keys[i], values[i]);
            }
        }
        keys = temp.keys;
        values = temp.values;
        M = temp.M;
    }

    // hash()对应的索引位置被占用,则放置到相邻的下一个位置,如果下一个位置也被占用,则
    // 放置到下下一个相邻位置
    public void put(K key, V value) {
        if (key == null)
            throw new NullPointerException("key不能为null");

        if (value == null) {
            remove(key);
            return;
        }

        if (N >= M / 2)
            resize(2 * M);

        int i;
        for (i = hash(key); keys[i] != null; i = (i + 1) % M) {
            if (keys[i].equals(key)) {
                values[i] = value;
                return;
            }
        }
        keys[i] = key;
        values[i] = value;
        N++;
    }

    public V get(K key) {
        if (key == null)
            throw new NullPointerException("参数不能为null");
        for (int i = hash(key); keys[i] != null; i = (i + 1) % M)
            if (keys[i].equals(key))
                return values[i];
        return null;
    }

    public void remove(K key) {
        if (key == null)
            throw new NullPointerException("key不能为null");
        if (!contains(key))
            return;

        int i = hash(key);
        while (!key.equals(keys[i])) {
            i = (i + 1) % M;
        }

        keys[i] = null;
        values[i] = null;

        // 重新散列位于同一个段里的所有键,否则,删除一个键之后,则与被删除的键有相同散列值的键即使存在于散列表中,但查找却有可能查找不到。
        // why? 大家可以参见存放键的方式,也即put方法
        i = (i + 1) % M;
        while (keys[i] != null) {
            K keyNeedToRehash = keys[i];
            V valueNeedToRehash = values[i];
            keys[i] = null;
            values[i] = null;
            N--;
            put(keyNeedToRehash, valueNeedToRehash);
            i = (i + 1) % M;
        }

        N--;

        if (N > 0 && N <= M / 8)
            resize(M / 2);

        assert isValid();
    }

    // 用于判断我们实现的散列表到底有没有与实现逻辑相互冲突
    private boolean isValid() {

        if (M < 2 * N) {
            System.err.println("散列表没有按照预期进行扩容");
            return false;
        }

        for (int i = 0; i < M; i++) {
            if (keys[i] == null)
                continue;
            else if (get(keys[i]) != values[i]) {
                System.err.println("存在数据不一致问题!");
                return false;
            }
        }
        return true;
    }

    public Iterable<K> keys() {
        Queue<K> queue = new LinkedList<K>();
        for (int i = 0; i < M; i++)
            if (keys[i] != null)
                queue.offer(keys[i]);
        return queue;
    }

    /**
     * 测试方法
     * 
     * @param args
     */
    public static void main(String... args) {
        LinearProbingHashMap<String, Integer> map = new LinearProbingHashMap<String, Integer>();

        map.put("lhever", 100);
        map.put("david", 100);
        map.put("kate", 100);
        System.out.println("this size is :" + map.size());

        map.remove("lhever");
        System.out.println("this size is :" + map.size());

        map.remove("david");
        map.remove("chris");
        System.out.println("this size is :" + map.size());

        map.put("kaven", 100);
        map.put("downald", 100);
        map.put("garry", 100);
        map.put("json", 100);

        for (String key : (Iterable<String>) map.keys()) {
            System.out.println(key + " : " + map.get(key));
        }

    }

}


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值