首页 > 代码库 > 键值表

键值表

什么是键值表

键值表是键值对集合,相似字典,支持存入键值对,按键查值等操作。

对外接口

  • public void put(Key key, Value val);
  • public Value get(Key key);
  • public boolean contains(Key key);
  • public Value remove(Key key);
  • public int size();
  • public boolean isEmpty();

接口代码

public interface IMap<Key, Value> {
    /**
     * 存入键值对
     * 
     * @param key
     *            键
     * @param value
     *            值
     */
    public void put(Key key, Value value);

    /**
     * 按鍵查值
     * 
     * @param key
     *            鍵
     * @return 值
     */
    public Value get(Key key);

    /**
     * 推断是否包括某键
     * 
     * @param key
     *            键
     * @return <code>true</code> 若包括;否则,<code>false</code>
     */
    public boolean contains(Key key);

    /**
     * 删除键为key的键值对
     * 
     * @param key
     *            键
     */
    public Value remove(Key key);

    /**
     * 返回键值对个数
     * 
     * @return 键值对个数
     */
    public int size();

    /**
     * 推断键值表是否为空
     * 
     * @return <code>true</code> 假设键值表为空;否则,<code>false</code>。

*/ public boolean isEmpty(); }

0基础实现

package com.gmail.dailyefforts.ds;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

public class SimpleLinkedMap<Key, Value> implements IMap<Key, Value>{
    private class Node {
        private Node prev;
        private Key key;
        private Value val;
        private Node next;

        public Node(Node prev, Key key, Value val, Node next) {
            this.prev = prev;
            this.key = key;
            this.val = val;
            this.next = next;
        }

        @Override
        public String toString() {
            return String.format("%s=%s", String.valueOf(key),
                    String.valueOf(val));
        }
    }

    private int size;
    private Node first;
    private Node last;

    @Override
    public void put(Key key, Value val) {
        for (Node x = first; x != null; x = x.next) {
            if (key.equals(x.key)) {
                x.val = val;
                return;
            }
        }
        Node oldLast = last;
        last = new Node(last, key, val, null);
        if (oldLast != null) {
            oldLast.next = last;
        }
        size++;
        if (first == null) {
            first = last;
        }
    }

    @Override
    public Value remove(Key key) {
        for (Node x = first; x != null; x = x.next) {
            if (key.equals(x.key)) {
                if (x.prev == null) {
                    first = x.next;
                } else {
                    x.prev.next = x.next;
                }
                if (x.next == null) {
                    last = x.prev;
                } else {
                    x.next.prev = x.prev;
                }
                size--;
                return x.val;
            }
        }
        return null;
    }

    @Override
    public boolean contains(Key key) {
        return get(key) != null;
    }

    @Override
    public Value get(Key key) {
        for (Node x = first; x != null; x = x.next) {
            if (key.equals(x.key)) {
                return x.val;
            }
        }
        return null;
    }

    @Override
    public int size() {
        return size;
    }


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

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append(‘{‘);
        for (Node x = first; x != null; x = x.next) {
            builder.append(x);
            if (x.next != null) {
                builder.append(", ");
            }
        }
        builder.append(‘}‘);
        return builder.toString();
    }

    public static void main(String[] args) {
        final int N = 100 * 100;
        SimpleLinkedMap<Integer, String> map = new SimpleLinkedMap<>();
        Map<Integer, String> map2 = new HashMap<>();
        for (int i = 0; i < N; i++) {
            Integer key = Integer.valueOf(i);
            String value = "item-" + i;
            map2.put(key, value);
            map.put(key, value);
        }
//      System.out.println(map2);
//      System.out.println(map);
        Random random = new Random(System.currentTimeMillis());
        for (int i = 0; i < N / 2; i++) {
            final int key = random.nextInt(N);
            final String a = map.remove(key);
            final String b = map2.remove(key);
            if (a == null) {
                assert (b == null);
            } else {
                assert (a.equals(b));
            }
        }

        assert(map.size() == map2.size());

        for (int i = 0; i < N; i++) {
            final String a = map.get(i);
            final String b = map2.get(i);
            if (a == null) {
                assert (b == null);
            } else {
                assert (a.equals(b));
            }
        }
    }

}

Hash实现

在上面的0基础实现中。每次查询都要遍历了整个字典。效率为O(n)。现实中,我们从字典中查询某个单词时。我们借助索引来提高效率。而不是从该字典收录的第一个词開始逐个遍历整个字典。
我们能够利用hash来建立索引,遇到索引同样时再用链表存储。

package com.gmail.dailyefforts.ds;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

public class MyHashMap<Key, Value> implements IMap<Key, Value> {
    private int size;
    private static final int M = 100 * 100;
    private SimpleLinkedMap<Key, Value>[] a = (SimpleLinkedMap<Key, Value>[]) new SimpleLinkedMap[M];

    @Override
    public void put(Key key, Value value) {
        SimpleLinkedMap<Key, Value> map = map(key);
        if (!map.contains(key)) {
            size++;
        }
        map.put(key, value);
    }

    private int hash(Key key) {
        // [0, M]
        return key.hashCode() & 0x7fffffff % M;
    }

    private SimpleLinkedMap<Key, Value> map(Key key) {
        int index = hash(key);
        if (a[index] == null) {
            a[index] = new SimpleLinkedMap<Key, Value>();
        }
        return a[index];
    }

    @Override
    public Value get(Key key) {
        return map(key).get(key);
    }

    @Override
    public boolean contains(Key key) {
        return map(key).contains(key);
    }

    @Override
    public Value remove(Key key) {
        Value value = map(key).remove(key);
        if (value != null) {
            size--;
        }
        return value;
    }

    @Override
    public int size() {
        return this.size;
    }

    @Override
    public String toString() {
        return super.toString();
    }

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

    public static void main(String[] args) {
        final int N = 100 * 100 * 100;
        MyHashMap<Integer, String> map = new MyHashMap<>();
        Map<Integer, String> mapRef = new HashMap<>();
        for (int i = 0; i < N; i++) {
            Integer key = Integer.valueOf(i);
            String value = "item-" + i;
            map.put(key, value);
            mapRef.put(key, value);
        }
        assert(map.size() == mapRef.size());

        Random random = new Random(System.currentTimeMillis());
        for (int i = 0; i < N / 2; i++) {
            final int key = random.nextInt(N);
            assert (map.contains(key) == mapRef.containsKey(key));
            final String a = map.remove(key);
            final String b = mapRef.remove(key);
            if (a == null) {
                assert (b == null);
            } else {
                assert (a.equals(b));
            }
        }

        assert (map.size() == mapRef.size());

        for (int i = 0; i < N; i++) {
            final int key = random.nextInt(N);
            final String a = map.get(key);
            final String b = mapRef.get(key);
            if (a == null) {
                assert (b == null);
            } else {
                assert (a.equals(b));
            }
        }

        System.out.println("test passed");
    }

}
<script type="text/javascript"> $(function () { $(‘pre.prettyprint code‘).each(function () { var lines = $(this).text().split(‘\n‘).length; var $numbering = $(‘
    ‘).addClass(‘pre-numbering‘).hide(); $(this).addClass(‘has-numbering‘).parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($(‘
  • ‘).text(i)); }; $numbering.fadeIn(1700); }); }); </script>

键值表